<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://mineplugin.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Tdiant</id>
	<title>Minecraft插件百科 - 用户贡献 [zh-cn]</title>
	<link rel="self" type="application/atom+xml" href="https://mineplugin.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Tdiant"/>
	<link rel="alternate" type="text/html" href="https://mineplugin.org/%E7%89%B9%E6%AE%8A:%E7%94%A8%E6%88%B7%E8%B4%A1%E7%8C%AE/Tdiant"/>
	<updated>2026-08-16T08:04:03Z</updated>
	<subtitle>用户贡献</subtitle>
	<generator>MediaWiki 1.41.1</generator>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5316</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5316"/>
		<updated>2017-08-01T11:46:44Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 默认值 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个能够帮助开发者快速解析与编辑配置文件的工具，它让配置文件更简单、更容易读写。无论是什么，这个API可以帮助你更容易的存储你插件的配置信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而这个API被设计有广泛的拓展性，这意味着它允许你实现其他需要实现的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API存在于 org.bukkit.configuration 和 org.bukkit.configuration.file 这两个包中。如果是创建在 1.1-R5 版本之前的插件，就只能用老版本并且与本文截然不同的API了，这些老版API在老版本的org.bukkit.util.configuration包中。这些内容彼此互不兼容，并且因为老的方法太过时了，所以在新版中已被删去。&lt;br /&gt;
&lt;br /&gt;
本文假定你对面向对象思想、Java与Bukkit插件编写的核心思想有着相当深的了解与认识。 本文并非JavaDoc[https://hub.spigotmc.org/javadocs/bukkit/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]的替代品。&lt;br /&gt;
&lt;br /&gt;
（译者注：上方链接因过时，已经替换到了最新的新版链接当中，内容稍微与原文有所不符，但影响不大）&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
当你的插件主类继承 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin] 类时也会继承父类 JavaPlugin 类的方法和属性。 在被继承的方法中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 文件。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次调用&#039;&#039;getConfig()&#039;&#039;方法时，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; 文件，同时从jar中的&#039;&#039;config.yml&#039;&#039;文件里加载相应的默认数据。 需要注意的是，在第一次使用之后若再次调用&#039;&#039;getConfig()&#039;&#039;方法，只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的，并且你对这个对象进行的修改并不会被保存，除非你手动保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;方法之后，对磁盘上的config.yml文件的修改不会被同步给这个对象。如果在 plugins 文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;文件, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;对象。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 方法的返回值分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;方法，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键   ===&lt;br /&gt;
&lt;br /&gt;
一个配置文件由键值对构成，键是一个字符串。键对应的值可能是一个 &#039;&#039;ConfigurationSection&#039;&#039; 对象或是其他形式的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回一个存有当前&#039;&#039;FileConfigurationSection&#039;&#039;所有键的集合，布尔值决定着是否递归地返回集合，如果值为true，将返回指定部分的所有键与所有对应子键, 如果为false则只会返回指定部分的所有键（不含子键）。 若要获取特定部分的所有键，需调用特定部分&#039;&#039;ConfigurationSection&#039;&#039;对象的 &#039;&#039;getKeys(boolean)&#039;&#039; 方法。 你可使用&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分。&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是一个字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 中使用路径（Path）来构成一个独立的键值对。 一个路径是一层层的键的组合，被用来联系所对应的值。 每一层都用路径分隔符区分开来，路径分隔符通常是 &#039;.&#039; (段落)。 举个例子，下面的YMAL文件有着下面这些组路径。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar中应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;文件。当&#039;&#039;config.yml&#039;&#039;丢失或是不完整的情况发生时，要获取的值将会从jar内的 &#039;&#039;config.yml&#039;&#039;文件来获取。被准备的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同目录下。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
有时如果你想添加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你对一个 &#039;&#039;FileConfiguration&#039;&#039; 对象进行了修改操作，或者改变了某些 &#039;&#039;Lists&#039;&#039;，如果你想保存你的修改，让插件被卸载了这些数据也能得以保存的话，你可以把你修改的数据保存到硬盘里。保存文件到硬盘里，你可以借助 saveConfig 方法。这会重写硬盘里的那份配置文件。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件来存储其他的配置信息，或储存额外的游戏信息配置文件等，你可以自己写一个你自己的方法，来仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 类里的 getConfig，reloadConfig，saveConfig 方法实现这一目的。下面是一个例子，可以告诉你如何编写一个方法，来读取和保存自定义配置文件。由于这些配置文件属于你的插件，你可以把这个方法放在你的主类，这样用起来跟原来一样。你必须为每个YAML文件写的一套方法。不过你可以使用以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 序列化与反序列化对象  ===&lt;br /&gt;
&lt;br /&gt;
在 Configuration API 中，上面提及的可以存储的对象均实现（implements）了 &#039;&#039;ConfigurationSerializable&#039;&#039; 接口。 对象序列化可以有助我们保存和读取数据，来让插件开发者集中开发插件的其他部分。 它真正的使一些任务变得简单明了了，就例如存储一个 &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; 对象到YAML文件当中， 开发者可以序列化一个包装类，用以提供取用 &#039;&#039;Location&#039;&#039; 对象的方法。&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, 除了implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
下面是一个使用了新版Configuration API的样例插件，它可以显示MOTD信息给进入服务器的玩家，并且可以让玩家使用指令获取规则（rule）。&#039;&#039;此处为了让代码行数达到最佳，代码没有采用适当的风格和插件布局&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // 如果没有默认 config.yml 文件，保存一个默认配置副本&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // 注册新的监听器&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // 当玩家进入服务器时发送 config.yml 文件里的信息&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // 给获取规则指令（rules）设置命令执行器（Command Executor）&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // 当指令被执行，发送 config.yml 里的规则信息给输入指令的人&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; 插件jar文件中的默认 config.yml 文件&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5315</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5315"/>
		<updated>2017-08-01T11:42:21Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 路径 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个能够帮助开发者快速解析与编辑配置文件的工具，它让配置文件更简单、更容易读写。无论是什么，这个API可以帮助你更容易的存储你插件的配置信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而这个API被设计有广泛的拓展性，这意味着它允许你实现其他需要实现的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API存在于 org.bukkit.configuration 和 org.bukkit.configuration.file 这两个包中。如果是创建在 1.1-R5 版本之前的插件，就只能用老版本并且与本文截然不同的API了，这些老版API在老版本的org.bukkit.util.configuration包中。这些内容彼此互不兼容，并且因为老的方法太过时了，所以在新版中已被删去。&lt;br /&gt;
&lt;br /&gt;
本文假定你对面向对象思想、Java与Bukkit插件编写的核心思想有着相当深的了解与认识。 本文并非JavaDoc[https://hub.spigotmc.org/javadocs/bukkit/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]的替代品。&lt;br /&gt;
&lt;br /&gt;
（译者注：上方链接因过时，已经替换到了最新的新版链接当中，内容稍微与原文有所不符，但影响不大）&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
当你的插件主类继承 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin] 类时也会继承父类 JavaPlugin 类的方法和属性。 在被继承的方法中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 文件。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次调用&#039;&#039;getConfig()&#039;&#039;方法时，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; 文件，同时从jar中的&#039;&#039;config.yml&#039;&#039;文件里加载相应的默认数据。 需要注意的是，在第一次使用之后若再次调用&#039;&#039;getConfig()&#039;&#039;方法，只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的，并且你对这个对象进行的修改并不会被保存，除非你手动保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;方法之后，对磁盘上的config.yml文件的修改不会被同步给这个对象。如果在 plugins 文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;文件, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;对象。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 方法的返回值分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;方法，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键   ===&lt;br /&gt;
&lt;br /&gt;
一个配置文件由键值对构成，键是一个字符串。键对应的值可能是一个 &#039;&#039;ConfigurationSection&#039;&#039; 对象或是其他形式的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回一个存有当前&#039;&#039;FileConfigurationSection&#039;&#039;所有键的集合，布尔值决定着是否递归地返回集合，如果值为true，将返回指定部分的所有键与所有对应子键, 如果为false则只会返回指定部分的所有键（不含子键）。 若要获取特定部分的所有键，需调用特定部分&#039;&#039;ConfigurationSection&#039;&#039;对象的 &#039;&#039;getKeys(boolean)&#039;&#039; 方法。 你可使用&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分。&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是一个字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 中使用路径（Path）来构成一个独立的键值对。 一个路径是一层层的键的组合，被用来联系所对应的值。 每一层都用路径分隔符区分开来，路径分隔符通常是 &#039;.&#039; (段落)。 举个例子，下面的YMAL文件有着下面这些组路径。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你对一个 &#039;&#039;FileConfiguration&#039;&#039; 对象进行了修改操作，或者改变了某些 &#039;&#039;Lists&#039;&#039;，如果你想保存你的修改，让插件被卸载了这些数据也能得以保存的话，你可以把你修改的数据保存到硬盘里。保存文件到硬盘里，你可以借助 saveConfig 方法。这会重写硬盘里的那份配置文件。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件来存储其他的配置信息，或储存额外的游戏信息配置文件等，你可以自己写一个你自己的方法，来仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 类里的 getConfig，reloadConfig，saveConfig 方法实现这一目的。下面是一个例子，可以告诉你如何编写一个方法，来读取和保存自定义配置文件。由于这些配置文件属于你的插件，你可以把这个方法放在你的主类，这样用起来跟原来一样。你必须为每个YAML文件写的一套方法。不过你可以使用以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 序列化与反序列化对象  ===&lt;br /&gt;
&lt;br /&gt;
在 Configuration API 中，上面提及的可以存储的对象均实现（implements）了 &#039;&#039;ConfigurationSerializable&#039;&#039; 接口。 对象序列化可以有助我们保存和读取数据，来让插件开发者集中开发插件的其他部分。 它真正的使一些任务变得简单明了了，就例如存储一个 &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; 对象到YAML文件当中， 开发者可以序列化一个包装类，用以提供取用 &#039;&#039;Location&#039;&#039; 对象的方法。&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, 除了implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
下面是一个使用了新版Configuration API的样例插件，它可以显示MOTD信息给进入服务器的玩家，并且可以让玩家使用指令获取规则（rule）。&#039;&#039;此处为了让代码行数达到最佳，代码没有采用适当的风格和插件布局&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // 如果没有默认 config.yml 文件，保存一个默认配置副本&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // 注册新的监听器&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // 当玩家进入服务器时发送 config.yml 文件里的信息&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // 给获取规则指令（rules）设置命令执行器（Command Executor）&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // 当指令被执行，发送 config.yml 里的规则信息给输入指令的人&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; 插件jar文件中的默认 config.yml 文件&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5314</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5314"/>
		<updated>2017-08-01T11:32:43Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​更正了“键”部分的译制内容&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个能够帮助开发者快速解析与编辑配置文件的工具，它让配置文件更简单、更容易读写。无论是什么，这个API可以帮助你更容易的存储你插件的配置信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而这个API被设计有广泛的拓展性，这意味着它允许你实现其他需要实现的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API存在于 org.bukkit.configuration 和 org.bukkit.configuration.file 这两个包中。如果是创建在 1.1-R5 版本之前的插件，就只能用老版本并且与本文截然不同的API了，这些老版API在老版本的org.bukkit.util.configuration包中。这些内容彼此互不兼容，并且因为老的方法太过时了，所以在新版中已被删去。&lt;br /&gt;
&lt;br /&gt;
本文假定你对面向对象思想、Java与Bukkit插件编写的核心思想有着相当深的了解与认识。 本文并非JavaDoc[https://hub.spigotmc.org/javadocs/bukkit/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]的替代品。&lt;br /&gt;
&lt;br /&gt;
（译者注：上方链接因过时，已经替换到了最新的新版链接当中，内容稍微与原文有所不符，但影响不大）&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
当你的插件主类继承 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin] 类时也会继承父类 JavaPlugin 类的方法和属性。 在被继承的方法中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 文件。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次调用&#039;&#039;getConfig()&#039;&#039;方法时，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; 文件，同时从jar中的&#039;&#039;config.yml&#039;&#039;文件里加载相应的默认数据。 需要注意的是，在第一次使用之后若再次调用&#039;&#039;getConfig()&#039;&#039;方法，只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的，并且你对这个对象进行的修改并不会被保存，除非你手动保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;方法之后，对磁盘上的config.yml文件的修改不会被同步给这个对象。如果在 plugins 文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;文件, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;对象。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 方法的返回值分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;方法，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键   ===&lt;br /&gt;
&lt;br /&gt;
一个配置文件由键值对构成，键是一个字符串。键对应的值可能是一个 &#039;&#039;ConfigurationSection&#039;&#039; 对象或是其他形式的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回一个存有当前&#039;&#039;FileConfigurationSection&#039;&#039;所有键的集合，布尔值决定着是否递归地返回集合，如果值为true，将返回指定部分的所有键与所有对应子键, 如果为false则只会返回指定部分的所有键（不含子键）。 若要获取特定部分的所有键，需调用特定部分&#039;&#039;ConfigurationSection&#039;&#039;对象的 &#039;&#039;getKeys(boolean)&#039;&#039; 方法。 你可使用&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分。&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是一个字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你对一个 &#039;&#039;FileConfiguration&#039;&#039; 对象进行了修改操作，或者改变了某些 &#039;&#039;Lists&#039;&#039;，如果你想保存你的修改，让插件被卸载了这些数据也能得以保存的话，你可以把你修改的数据保存到硬盘里。保存文件到硬盘里，你可以借助 saveConfig 方法。这会重写硬盘里的那份配置文件。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件来存储其他的配置信息，或储存额外的游戏信息配置文件等，你可以自己写一个你自己的方法，来仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 类里的 getConfig，reloadConfig，saveConfig 方法实现这一目的。下面是一个例子，可以告诉你如何编写一个方法，来读取和保存自定义配置文件。由于这些配置文件属于你的插件，你可以把这个方法放在你的主类，这样用起来跟原来一样。你必须为每个YAML文件写的一套方法。不过你可以使用以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 序列化与反序列化对象  ===&lt;br /&gt;
&lt;br /&gt;
在 Configuration API 中，上面提及的可以存储的对象均实现（implements）了 &#039;&#039;ConfigurationSerializable&#039;&#039; 接口。 对象序列化可以有助我们保存和读取数据，来让插件开发者集中开发插件的其他部分。 它真正的使一些任务变得简单明了了，就例如存储一个 &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; 对象到YAML文件当中， 开发者可以序列化一个包装类，用以提供取用 &#039;&#039;Location&#039;&#039; 对象的方法。&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, 除了implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
下面是一个使用了新版Configuration API的样例插件，它可以显示MOTD信息给进入服务器的玩家，并且可以让玩家使用指令获取规则（rule）。&#039;&#039;此处为了让代码行数达到最佳，代码没有采用适当的风格和插件布局&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // 如果没有默认 config.yml 文件，保存一个默认配置副本&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // 注册新的监听器&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // 当玩家进入服务器时发送 config.yml 文件里的信息&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // 给获取规则指令（rules）设置命令执行器（Command Executor）&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // 当指令被执行，发送 config.yml 里的规则信息给输入指令的人&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; 插件jar文件中的默认 config.yml 文件&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5313</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5313"/>
		<updated>2017-08-01T11:21:10Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Serializing and Deserializing Objects */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个能够帮助开发者快速解析与编辑配置文件的工具，它让配置文件更简单、更容易读写。无论是什么，这个API可以帮助你更容易的存储你插件的配置信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而这个API被设计有广泛的拓展性，这意味着它允许你实现其他需要实现的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API存在于 org.bukkit.configuration 和 org.bukkit.configuration.file 这两个包中。如果是创建在 1.1-R5 版本之前的插件，就只能用老版本并且与本文截然不同的API了，这些老版API在老版本的org.bukkit.util.configuration包中。这些内容彼此互不兼容，并且因为老的方法太过时了，所以在新版中已被删去。&lt;br /&gt;
&lt;br /&gt;
本文假定你对面向对象思想、Java与Bukkit插件编写的核心思想有着相当深的了解与认识。 本文并非JavaDoc[https://hub.spigotmc.org/javadocs/bukkit/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]的替代品。&lt;br /&gt;
&lt;br /&gt;
（译者注：上方链接因过时，已经替换到了最新的新版链接当中，内容稍微与原文有所不符，但影响不大）&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin] 类时也会继承父类 JavaPlugin 类的方法和属性。 在被继承的方法中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 文件。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次调用&#039;&#039;getConfig()&#039;&#039;方法时，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; 文件，同时从jar中的&#039;&#039;config.yml&#039;&#039;文件里加载相应的默认数据。 需要注意的是，在第一次使用之后若再次调用&#039;&#039;getConfig()&#039;&#039;方法，只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的，并且你对这个对象进行的修改并不会被保存，除非你手动保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;方法之后，对磁盘上的config.yml文件的修改不会被同步给这个对象。如果在 plugins 文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;文件, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;对象。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 方法的返回值分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;方法，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串。值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你对一个 &#039;&#039;FileConfiguration&#039;&#039; 对象进行了修改操作，或者改变了某些 &#039;&#039;Lists&#039;&#039;，如果你想保存你的修改，让插件被卸载了这些数据也能得以保存的话，你可以把你修改的数据保存到硬盘里。保存文件到硬盘里，你可以借助 saveConfig 方法。这会重写硬盘里的那份配置文件。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件来存储其他的配置信息，或储存额外的游戏信息配置文件等，你可以自己写一个你自己的方法，来仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 类里的 getConfig，reloadConfig，saveConfig 方法实现这一目的。下面是一个例子，可以告诉你如何编写一个方法，来读取和保存自定义配置文件。由于这些配置文件属于你的插件，你可以把这个方法放在你的主类，这样用起来跟原来一样。你必须为每个YAML文件写的一套方法。不过你可以使用以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 序列化与反序列化对象  ===&lt;br /&gt;
&lt;br /&gt;
在 Configuration API 中，上面提及的可以存储的对象均实现（implements）了 &#039;&#039;ConfigurationSerializable&#039;&#039; 接口。 对象序列化可以有助我们保存和读取数据，来让插件开发者集中开发插件的其他部分。 它真正的使一些任务变得简单明了了，就例如存储一个 &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; 对象到YAML文件当中， 开发者可以序列化一个包装类，用以提供取用 &#039;&#039;Location&#039;&#039; 对象的方法。&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, 除了implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
下面是一个使用了新版Configuration API的样例插件，它可以显示MOTD信息给进入服务器的玩家，并且可以让玩家使用指令获取规则（rule）。&#039;&#039;此处为了让代码行数达到最佳，代码没有采用适当的风格和插件布局&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // 如果没有默认 config.yml 文件，保存一个默认配置副本&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // 注册新的监听器&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // 当玩家进入服务器时发送 config.yml 文件里的信息&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // 给获取规则指令（rules）设置命令执行器（Command Executor）&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // 当指令被执行，发送 config.yml 里的规则信息给输入指令的人&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; 插件jar文件中的默认 config.yml 文件&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5312</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=5312"/>
		<updated>2017-08-01T11:10:09Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​更正了几处不当错误，做了简单的汉化工作&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个能够帮助开发者快速解析与编辑配置文件的工具，它让配置文件更简单、更容易读写。无论是什么，这个API可以帮助你更容易的存储你插件的配置信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而这个API被设计有广泛的拓展性，这意味着它允许你实现其他需要实现的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API存在于 org.bukkit.configuration 和 org.bukkit.configuration.file 这两个包中。如果是创建在 1.1-R5 版本之前的插件，就只能用老版本并且与本文截然不同的API了，这些老版API在老版本的org.bukkit.util.configuration包中。这些内容彼此互不兼容，并且因为老的方法太过时了，所以在新版中已被删去。&lt;br /&gt;
&lt;br /&gt;
本文假定你对面向对象思想、Java与Bukkit插件编写的核心思想有着相当深的了解与认识。 本文并非JavaDoc[https://hub.spigotmc.org/javadocs/bukkit/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]的替代品。&lt;br /&gt;
&lt;br /&gt;
（译者注：上方链接因过时，已经替换到了最新的新版链接当中，内容稍微与原文有所不符，但影响不大）&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin] 类时也会继承父类 JavaPlugin 类的方法和属性。 在被继承的方法中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 文件。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次调用&#039;&#039;getConfig()&#039;&#039;方法时，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; 文件，同时从jar中的&#039;&#039;config.yml&#039;&#039;文件里加载相应的默认数据。 需要注意的是，在第一次使用之后若再次调用&#039;&#039;getConfig()&#039;&#039;方法，只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的，并且你对这个对象进行的修改并不会被保存，除非你手动保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;方法之后，对磁盘上的config.yml文件的修改不会被同步给这个对象。如果在 plugins 文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;文件, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;对象。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 方法的返回值分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;方法，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串。值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你对一个 &#039;&#039;FileConfiguration&#039;&#039; 对象进行了修改操作，或者改变了某些 &#039;&#039;Lists&#039;&#039;，如果你想保存你的修改，让插件被卸载了这些数据也能得以保存的话，你可以把你修改的数据保存到硬盘里。保存文件到硬盘里，你可以借助 saveConfig 方法。这会重写硬盘里的那份配置文件。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件来存储其他的配置信息，或储存额外的游戏信息配置文件等，你可以自己写一个你自己的方法，来仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 类里的 getConfig，reloadConfig，saveConfig 方法实现这一目的。下面是一个例子，可以告诉你如何编写一个方法，来读取和保存自定义配置文件。由于这些配置文件属于你的插件，你可以把这个方法放在你的主类，这样用起来跟原来一样。你必须为每个YAML文件写的一套方法。不过你可以使用以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
下面是一个使用了新版Configuration API的样例插件，它可以显示MOTD信息给进入服务器的玩家，并且可以让玩家使用指令获取规则（rule）。&#039;&#039;此处为了让代码行数达到最佳，代码没有采用适当的风格和插件布局&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // 如果没有默认 config.yml 文件，保存一个默认配置副本&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // 注册新的监听器&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // 当玩家进入服务器时发送 config.yml 文件里的信息&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // 给获取规则指令（rules）设置命令执行器（Command Executor）&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // 当指令被执行，发送 config.yml 里的规则信息给输入指令的人&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; 插件jar文件中的默认 config.yml 文件&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5311</id>
		<title>Event API Reference/en</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5311"/>
		<updated>2017-08-01T10:41:25Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Video Tutorial */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{条目原文}}&lt;br /&gt;
欢迎来到Bukkit提供的EventAPI相关教程页&lt;br /&gt;
&lt;br /&gt;
== 简介 ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;事件&#039;&#039;&#039; 就是你在使用CraftBukkit,或者基于CraftBukkit服务端的其他服务端的时候，服务端会将服务器内发生的事告诉正在监听对应事件的插件。CraftBukkit服务端为插件定义了许多不同的事件，比如玩家登录服务器，玩家进入服务器，玩家点击方块，玩家重生。还有一些可以被取消的事件例如，玩家放置方块，玩家破坏方块，等等。更还有区块卸载区块装载，实体等相关事件。具体请查看API文档:&lt;br /&gt;
BukkitAPI:https://hub.spigotmc.org/javadocs/bukkit&lt;br /&gt;
SpigotAPI:https://hub.spigotmc.org/javadocs/spigot&lt;br /&gt;
这里请注意了,有些事件是Spigot独有事件如果你想兼容CraftBukkit服务端请勿调用,例如PlayerSpawnLocationEvent&lt;br /&gt;
&lt;br /&gt;
== 开始使用EventAPI ==&lt;br /&gt;
为了让大家看得懂，这一部分先定个小目标,先只用PlayerLoginEvent做示范&lt;br /&gt;
&lt;br /&gt;
=== 设置监听指定Event的方法 ===&lt;br /&gt;
为了监听事件，我们需要创建一个新的方法，并给它加上EventHandler的注解来告诉服务端，这是个监听事件的方法,服务端就会在事件发生的时候调用该方法&lt;br /&gt;
方法名可以自己写你自己喜欢的，但传入的参数必须是你想监听的事件类&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你在监听到玩家登录时执行的代码&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
&amp;quot;@EventHandler&amp;quot; 是一个注解，用来告诉服务端这是一个监听事件的方法,你只需要把它加在你监听指定事件方法的上面就好，就像下面这样&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // EventPriority.NORMAL 是默认的监听级别&amp;lt;/source&amp;gt;&lt;br /&gt;
这样就告诉了服务端你这个监听事件的方法，优先级是普通的。&lt;br /&gt;
&lt;br /&gt;
当然你也可以自己设置事件监听的优先级比如下面这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 将该事件监听的优先级修改到HIGHEST级别&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 将该事件的优先级修改到LOW级别&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在呢你的监听事件的方法的代码应该像这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // Your code here...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加一个监听器 ===&lt;br /&gt;
为了监听事件，你必须让监听事件的类实现 &#039;&#039;Listener&#039;&#039; (包名是:org.bukkit.event.Listener) 这个接口, e.g.:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
上面那个名字叫&#039;&#039;onLogin&#039;&#039; 的监听玩家登录事件的方法现在什么都不能干&lt;br /&gt;
&lt;br /&gt;
就像我说的，Bukkit通过EventHandler注解来知道你这个方法是用来监听事件的，通过方法需要传入的参数知道要监听的是哪个事件，比如: &amp;lt;code&amp;gt;PlayerLoginEvent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{特别提醒:要想监听事件，你还必须得执行一个注册监听事件类的方法才能让插件正常监听事件}}&lt;br /&gt;
&lt;br /&gt;
你必须在你的插件主类里(也就是那个继承JavaPlugin的类)的onEnable方法里加上如下代码:&lt;br /&gt;
其实吧也不是非得在onEnable里面加上，在你需要监听事件的时候保证这个监听事件的类被注册过就行&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    System.out.println(event.getPlayer().getName()+&amp;quot;登入了服务器&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
这里我提醒一下各位读者&lt;br /&gt;
玩家登录服务器分四个阶段&lt;br /&gt;
HandShake(和服务器握手&lt;br /&gt;
PreLogin(预登录阶段，就是正在登录&lt;br /&gt;
Login(登入阶段,一般盗版服会直接跳过该阶段,但是是可以监听到的&lt;br /&gt;
JoinGame(成功登录服务器&lt;br /&gt;
&lt;br /&gt;
=== EventHandler注解的参数 ===&lt;br /&gt;
@EventHandler你可以使用注解来定义事件监听的相关设置&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Description&lt;br /&gt;
! Values&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| 监听事件的优先级&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| 如果你把它设置成true，如果事件被取消这个监听的事件将不再会被监听到&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
Bukkit会按照下面的顺序挨个在事件发生时通知监听该事件的插件&lt;br /&gt;
* EventPriority.LOWEST(最优先&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR(最后&lt;br /&gt;
&lt;br /&gt;
服务端将会根据这些监听器内EventHandler注解里面的监听优先级按顺序通知监听器，从LOWEST到MONITOR挨个通知插件，你可以通过设置优先级来阻止其他的插件对同等事件进行操作，并在MONITOR优先级的监听器取消后面插件对该事件的监听达到覆盖操作。&lt;br /&gt;
&lt;br /&gt;
== 注册监听事件的类 ==&lt;br /&gt;
要使你插件里包含监听器方法的类被注册，这个监听器才能起效，且包含监听方法的这个类必须实现Listener这个接口&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你只需要这一行代码来注册监听事件的类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听事件的例子 ===&lt;br /&gt;
这个监听事件的类包含两个监听器，且监听的是同一个事件，但是一个优先级是HIGH一个是默认的NORMAL&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 将监听事件的类在你的插件内起效 ===&lt;br /&gt;
下面代码示范了如何注册你的监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 直接使用插件主类作为监听类 ====&lt;br /&gt;
下面的代码示范了如何偷懒直接使用插件主类作为监听类并注册它:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 用监听类的构造器注册监听类 ===&lt;br /&gt;
有很多很多不同的方式来注册你的监听类，下面代码示范了如何使用监听类的构造器来注册监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在插件的主类代码应该看起来像这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 注销你的监听器 ==&lt;br /&gt;
前面提到了如何去创建并且注册监听器使之生效，同样的服务端也提供了注销监听器的方法，你可以注销一个插件里所有的监听器，也可以注销一个插件里所有指定事件的监听器&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件内指定事件的监听器 ====&lt;br /&gt;
无一例外，所有事件都有一个静态方法:getHandlerList()，你可以使用这个方法下面的unregister()方法来对一个插件内所有的这个事件的监听器进行注销&lt;br /&gt;
比如这样搞:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// 这样就代表会注销plugin这个插件内所有监听PlayerInteractEvent这个事件的监听器&lt;br /&gt;
// 当然了你也可以单独在plugin那个参数里面写入要注销指定监听器的类&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
下面我们将会学习自定义事件&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件所有的监听器 ====&lt;br /&gt;
你可以使用HandlerList类下面的unregisterAll()这个静态方法来对一个插件所监听的所有事件的监听器进行注销&lt;br /&gt;
比如下面这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// 这将会注销这个插件监听这个事件的所有监听器&lt;br /&gt;
// 当然这里的参数也可以用一个指定的监听类来代替，可以单独注销一个监听器&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
要创建一个自定义的事件是很简单的，而且不会和Bukkit所自带的事件冲突&lt;br /&gt;
&lt;br /&gt;
要创建你的自定义事件，有下面的几点是需要注意的&lt;br /&gt;
你的自定义事件的类必须继承Event类，并且必须要有&lt;br /&gt;
HandlerList这个静态常量和getHandlers这个方法&lt;br /&gt;
所以你的一个基础的自定义事件的类应该看起来像下面这样:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
因为为了不和别的事件起冲突，你必须要拥有自己的HandlerList才可以.&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件的一个小例子 ===&lt;br /&gt;
下面得到代码示范了如何快速简单的创建一个自定义的事件的类:&lt;br /&gt;
CustomEvent是类名，可以根据自己的事件类型进行更改&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 触发你的自定义事件 ====&lt;br /&gt;
你现在可以自如掌握如何创建与触发你的自定义事件了。下面是一个例子：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建你的自定义事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 现在来对你的事件做点什么&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
请您记住：你现在正在控制你的事件，如果你不去触发他，他就没有办法正常工作，什么都不会发生！&lt;br /&gt;
&lt;br /&gt;
==== 监听自定义事件 ====&lt;br /&gt;
如何来监听一个自定义事件呢？ 通常情况下，监听自定义事件与其他普通的事件是一样的！&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 让你的自定义事件可以被取消 ====&lt;br /&gt;
如果你希望你的自定义事件可以被取消，记住，要&amp;quot;implements Cancellable.&amp;quot;（实现Cancelled类），就像你去实现Listener类一样，是非常简单的，下面是一个例子：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
之后你需要留意，在做些什么之前，你需要在你的代码中检查是否有插件取消了这个事件。就像这样：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建一个事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 检查他是不是被取消了&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // 现在你就可以做些什么了！&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 视频资料 ==&lt;br /&gt;
如果你更希望观看视频版的本教程，请点击 [http://www.youtube.com/watch?v=PWQNsqwD-AY 这里！].&lt;br /&gt;
&lt;br /&gt;
[[Category: 条目原文]] [[分类:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5310</id>
		<title>Event API Reference/en</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5310"/>
		<updated>2017-08-01T10:40:22Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Making your CustomEvent Cancellable */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{条目原文}}&lt;br /&gt;
欢迎来到Bukkit提供的EventAPI相关教程页&lt;br /&gt;
&lt;br /&gt;
== 简介 ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;事件&#039;&#039;&#039; 就是你在使用CraftBukkit,或者基于CraftBukkit服务端的其他服务端的时候，服务端会将服务器内发生的事告诉正在监听对应事件的插件。CraftBukkit服务端为插件定义了许多不同的事件，比如玩家登录服务器，玩家进入服务器，玩家点击方块，玩家重生。还有一些可以被取消的事件例如，玩家放置方块，玩家破坏方块，等等。更还有区块卸载区块装载，实体等相关事件。具体请查看API文档:&lt;br /&gt;
BukkitAPI:https://hub.spigotmc.org/javadocs/bukkit&lt;br /&gt;
SpigotAPI:https://hub.spigotmc.org/javadocs/spigot&lt;br /&gt;
这里请注意了,有些事件是Spigot独有事件如果你想兼容CraftBukkit服务端请勿调用,例如PlayerSpawnLocationEvent&lt;br /&gt;
&lt;br /&gt;
== 开始使用EventAPI ==&lt;br /&gt;
为了让大家看得懂，这一部分先定个小目标,先只用PlayerLoginEvent做示范&lt;br /&gt;
&lt;br /&gt;
=== 设置监听指定Event的方法 ===&lt;br /&gt;
为了监听事件，我们需要创建一个新的方法，并给它加上EventHandler的注解来告诉服务端，这是个监听事件的方法,服务端就会在事件发生的时候调用该方法&lt;br /&gt;
方法名可以自己写你自己喜欢的，但传入的参数必须是你想监听的事件类&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你在监听到玩家登录时执行的代码&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
&amp;quot;@EventHandler&amp;quot; 是一个注解，用来告诉服务端这是一个监听事件的方法,你只需要把它加在你监听指定事件方法的上面就好，就像下面这样&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // EventPriority.NORMAL 是默认的监听级别&amp;lt;/source&amp;gt;&lt;br /&gt;
这样就告诉了服务端你这个监听事件的方法，优先级是普通的。&lt;br /&gt;
&lt;br /&gt;
当然你也可以自己设置事件监听的优先级比如下面这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 将该事件监听的优先级修改到HIGHEST级别&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 将该事件的优先级修改到LOW级别&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在呢你的监听事件的方法的代码应该像这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // Your code here...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加一个监听器 ===&lt;br /&gt;
为了监听事件，你必须让监听事件的类实现 &#039;&#039;Listener&#039;&#039; (包名是:org.bukkit.event.Listener) 这个接口, e.g.:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
上面那个名字叫&#039;&#039;onLogin&#039;&#039; 的监听玩家登录事件的方法现在什么都不能干&lt;br /&gt;
&lt;br /&gt;
就像我说的，Bukkit通过EventHandler注解来知道你这个方法是用来监听事件的，通过方法需要传入的参数知道要监听的是哪个事件，比如: &amp;lt;code&amp;gt;PlayerLoginEvent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{特别提醒:要想监听事件，你还必须得执行一个注册监听事件类的方法才能让插件正常监听事件}}&lt;br /&gt;
&lt;br /&gt;
你必须在你的插件主类里(也就是那个继承JavaPlugin的类)的onEnable方法里加上如下代码:&lt;br /&gt;
其实吧也不是非得在onEnable里面加上，在你需要监听事件的时候保证这个监听事件的类被注册过就行&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    System.out.println(event.getPlayer().getName()+&amp;quot;登入了服务器&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
这里我提醒一下各位读者&lt;br /&gt;
玩家登录服务器分四个阶段&lt;br /&gt;
HandShake(和服务器握手&lt;br /&gt;
PreLogin(预登录阶段，就是正在登录&lt;br /&gt;
Login(登入阶段,一般盗版服会直接跳过该阶段,但是是可以监听到的&lt;br /&gt;
JoinGame(成功登录服务器&lt;br /&gt;
&lt;br /&gt;
=== EventHandler注解的参数 ===&lt;br /&gt;
@EventHandler你可以使用注解来定义事件监听的相关设置&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Description&lt;br /&gt;
! Values&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| 监听事件的优先级&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| 如果你把它设置成true，如果事件被取消这个监听的事件将不再会被监听到&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
Bukkit会按照下面的顺序挨个在事件发生时通知监听该事件的插件&lt;br /&gt;
* EventPriority.LOWEST(最优先&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR(最后&lt;br /&gt;
&lt;br /&gt;
服务端将会根据这些监听器内EventHandler注解里面的监听优先级按顺序通知监听器，从LOWEST到MONITOR挨个通知插件，你可以通过设置优先级来阻止其他的插件对同等事件进行操作，并在MONITOR优先级的监听器取消后面插件对该事件的监听达到覆盖操作。&lt;br /&gt;
&lt;br /&gt;
== 注册监听事件的类 ==&lt;br /&gt;
要使你插件里包含监听器方法的类被注册，这个监听器才能起效，且包含监听方法的这个类必须实现Listener这个接口&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你只需要这一行代码来注册监听事件的类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听事件的例子 ===&lt;br /&gt;
这个监听事件的类包含两个监听器，且监听的是同一个事件，但是一个优先级是HIGH一个是默认的NORMAL&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 将监听事件的类在你的插件内起效 ===&lt;br /&gt;
下面代码示范了如何注册你的监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 直接使用插件主类作为监听类 ====&lt;br /&gt;
下面的代码示范了如何偷懒直接使用插件主类作为监听类并注册它:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 用监听类的构造器注册监听类 ===&lt;br /&gt;
有很多很多不同的方式来注册你的监听类，下面代码示范了如何使用监听类的构造器来注册监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在插件的主类代码应该看起来像这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 注销你的监听器 ==&lt;br /&gt;
前面提到了如何去创建并且注册监听器使之生效，同样的服务端也提供了注销监听器的方法，你可以注销一个插件里所有的监听器，也可以注销一个插件里所有指定事件的监听器&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件内指定事件的监听器 ====&lt;br /&gt;
无一例外，所有事件都有一个静态方法:getHandlerList()，你可以使用这个方法下面的unregister()方法来对一个插件内所有的这个事件的监听器进行注销&lt;br /&gt;
比如这样搞:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// 这样就代表会注销plugin这个插件内所有监听PlayerInteractEvent这个事件的监听器&lt;br /&gt;
// 当然了你也可以单独在plugin那个参数里面写入要注销指定监听器的类&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
下面我们将会学习自定义事件&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件所有的监听器 ====&lt;br /&gt;
你可以使用HandlerList类下面的unregisterAll()这个静态方法来对一个插件所监听的所有事件的监听器进行注销&lt;br /&gt;
比如下面这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// 这将会注销这个插件监听这个事件的所有监听器&lt;br /&gt;
// 当然这里的参数也可以用一个指定的监听类来代替，可以单独注销一个监听器&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
要创建一个自定义的事件是很简单的，而且不会和Bukkit所自带的事件冲突&lt;br /&gt;
&lt;br /&gt;
要创建你的自定义事件，有下面的几点是需要注意的&lt;br /&gt;
你的自定义事件的类必须继承Event类，并且必须要有&lt;br /&gt;
HandlerList这个静态常量和getHandlers这个方法&lt;br /&gt;
所以你的一个基础的自定义事件的类应该看起来像下面这样:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
因为为了不和别的事件起冲突，你必须要拥有自己的HandlerList才可以.&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件的一个小例子 ===&lt;br /&gt;
下面得到代码示范了如何快速简单的创建一个自定义的事件的类:&lt;br /&gt;
CustomEvent是类名，可以根据自己的事件类型进行更改&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 触发你的自定义事件 ====&lt;br /&gt;
你现在可以自如掌握如何创建与触发你的自定义事件了。下面是一个例子：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建你的自定义事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 现在来对你的事件做点什么&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
请您记住：你现在正在控制你的事件，如果你不去触发他，他就没有办法正常工作，什么都不会发生！&lt;br /&gt;
&lt;br /&gt;
==== 监听自定义事件 ====&lt;br /&gt;
如何来监听一个自定义事件呢？ 通常情况下，监听自定义事件与其他普通的事件是一样的！&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 让你的自定义事件可以被取消 ====&lt;br /&gt;
如果你希望你的自定义事件可以被取消，记住，要&amp;quot;implements Cancellable.&amp;quot;（实现Cancelled类），就像你去实现Listener类一样，是非常简单的，下面是一个例子：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
之后你需要留意，在做些什么之前，你需要在你的代码中检查是否有插件取消了这个事件。就像这样：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建一个事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 检查他是不是被取消了&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // 现在你就可以做些什么了！&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Video Tutorial ==&lt;br /&gt;
If you would prefer to watch a video tutorial version of this, please click [http://www.youtube.com/watch?v=PWQNsqwD-AY here].&lt;br /&gt;
&lt;br /&gt;
[[Category: 条目原文]] [[分类:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5309</id>
		<title>Event API Reference/en</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5309"/>
		<updated>2017-08-01T10:36:56Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Listening to a Custom Event */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{条目原文}}&lt;br /&gt;
欢迎来到Bukkit提供的EventAPI相关教程页&lt;br /&gt;
&lt;br /&gt;
== 简介 ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;事件&#039;&#039;&#039; 就是你在使用CraftBukkit,或者基于CraftBukkit服务端的其他服务端的时候，服务端会将服务器内发生的事告诉正在监听对应事件的插件。CraftBukkit服务端为插件定义了许多不同的事件，比如玩家登录服务器，玩家进入服务器，玩家点击方块，玩家重生。还有一些可以被取消的事件例如，玩家放置方块，玩家破坏方块，等等。更还有区块卸载区块装载，实体等相关事件。具体请查看API文档:&lt;br /&gt;
BukkitAPI:https://hub.spigotmc.org/javadocs/bukkit&lt;br /&gt;
SpigotAPI:https://hub.spigotmc.org/javadocs/spigot&lt;br /&gt;
这里请注意了,有些事件是Spigot独有事件如果你想兼容CraftBukkit服务端请勿调用,例如PlayerSpawnLocationEvent&lt;br /&gt;
&lt;br /&gt;
== 开始使用EventAPI ==&lt;br /&gt;
为了让大家看得懂，这一部分先定个小目标,先只用PlayerLoginEvent做示范&lt;br /&gt;
&lt;br /&gt;
=== 设置监听指定Event的方法 ===&lt;br /&gt;
为了监听事件，我们需要创建一个新的方法，并给它加上EventHandler的注解来告诉服务端，这是个监听事件的方法,服务端就会在事件发生的时候调用该方法&lt;br /&gt;
方法名可以自己写你自己喜欢的，但传入的参数必须是你想监听的事件类&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你在监听到玩家登录时执行的代码&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
&amp;quot;@EventHandler&amp;quot; 是一个注解，用来告诉服务端这是一个监听事件的方法,你只需要把它加在你监听指定事件方法的上面就好，就像下面这样&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // EventPriority.NORMAL 是默认的监听级别&amp;lt;/source&amp;gt;&lt;br /&gt;
这样就告诉了服务端你这个监听事件的方法，优先级是普通的。&lt;br /&gt;
&lt;br /&gt;
当然你也可以自己设置事件监听的优先级比如下面这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 将该事件监听的优先级修改到HIGHEST级别&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 将该事件的优先级修改到LOW级别&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在呢你的监听事件的方法的代码应该像这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // Your code here...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加一个监听器 ===&lt;br /&gt;
为了监听事件，你必须让监听事件的类实现 &#039;&#039;Listener&#039;&#039; (包名是:org.bukkit.event.Listener) 这个接口, e.g.:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
上面那个名字叫&#039;&#039;onLogin&#039;&#039; 的监听玩家登录事件的方法现在什么都不能干&lt;br /&gt;
&lt;br /&gt;
就像我说的，Bukkit通过EventHandler注解来知道你这个方法是用来监听事件的，通过方法需要传入的参数知道要监听的是哪个事件，比如: &amp;lt;code&amp;gt;PlayerLoginEvent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{特别提醒:要想监听事件，你还必须得执行一个注册监听事件类的方法才能让插件正常监听事件}}&lt;br /&gt;
&lt;br /&gt;
你必须在你的插件主类里(也就是那个继承JavaPlugin的类)的onEnable方法里加上如下代码:&lt;br /&gt;
其实吧也不是非得在onEnable里面加上，在你需要监听事件的时候保证这个监听事件的类被注册过就行&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    System.out.println(event.getPlayer().getName()+&amp;quot;登入了服务器&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
这里我提醒一下各位读者&lt;br /&gt;
玩家登录服务器分四个阶段&lt;br /&gt;
HandShake(和服务器握手&lt;br /&gt;
PreLogin(预登录阶段，就是正在登录&lt;br /&gt;
Login(登入阶段,一般盗版服会直接跳过该阶段,但是是可以监听到的&lt;br /&gt;
JoinGame(成功登录服务器&lt;br /&gt;
&lt;br /&gt;
=== EventHandler注解的参数 ===&lt;br /&gt;
@EventHandler你可以使用注解来定义事件监听的相关设置&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Description&lt;br /&gt;
! Values&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| 监听事件的优先级&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| 如果你把它设置成true，如果事件被取消这个监听的事件将不再会被监听到&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
Bukkit会按照下面的顺序挨个在事件发生时通知监听该事件的插件&lt;br /&gt;
* EventPriority.LOWEST(最优先&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR(最后&lt;br /&gt;
&lt;br /&gt;
服务端将会根据这些监听器内EventHandler注解里面的监听优先级按顺序通知监听器，从LOWEST到MONITOR挨个通知插件，你可以通过设置优先级来阻止其他的插件对同等事件进行操作，并在MONITOR优先级的监听器取消后面插件对该事件的监听达到覆盖操作。&lt;br /&gt;
&lt;br /&gt;
== 注册监听事件的类 ==&lt;br /&gt;
要使你插件里包含监听器方法的类被注册，这个监听器才能起效，且包含监听方法的这个类必须实现Listener这个接口&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你只需要这一行代码来注册监听事件的类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听事件的例子 ===&lt;br /&gt;
这个监听事件的类包含两个监听器，且监听的是同一个事件，但是一个优先级是HIGH一个是默认的NORMAL&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 将监听事件的类在你的插件内起效 ===&lt;br /&gt;
下面代码示范了如何注册你的监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 直接使用插件主类作为监听类 ====&lt;br /&gt;
下面的代码示范了如何偷懒直接使用插件主类作为监听类并注册它:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 用监听类的构造器注册监听类 ===&lt;br /&gt;
有很多很多不同的方式来注册你的监听类，下面代码示范了如何使用监听类的构造器来注册监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在插件的主类代码应该看起来像这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 注销你的监听器 ==&lt;br /&gt;
前面提到了如何去创建并且注册监听器使之生效，同样的服务端也提供了注销监听器的方法，你可以注销一个插件里所有的监听器，也可以注销一个插件里所有指定事件的监听器&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件内指定事件的监听器 ====&lt;br /&gt;
无一例外，所有事件都有一个静态方法:getHandlerList()，你可以使用这个方法下面的unregister()方法来对一个插件内所有的这个事件的监听器进行注销&lt;br /&gt;
比如这样搞:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// 这样就代表会注销plugin这个插件内所有监听PlayerInteractEvent这个事件的监听器&lt;br /&gt;
// 当然了你也可以单独在plugin那个参数里面写入要注销指定监听器的类&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
下面我们将会学习自定义事件&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件所有的监听器 ====&lt;br /&gt;
你可以使用HandlerList类下面的unregisterAll()这个静态方法来对一个插件所监听的所有事件的监听器进行注销&lt;br /&gt;
比如下面这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// 这将会注销这个插件监听这个事件的所有监听器&lt;br /&gt;
// 当然这里的参数也可以用一个指定的监听类来代替，可以单独注销一个监听器&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
要创建一个自定义的事件是很简单的，而且不会和Bukkit所自带的事件冲突&lt;br /&gt;
&lt;br /&gt;
要创建你的自定义事件，有下面的几点是需要注意的&lt;br /&gt;
你的自定义事件的类必须继承Event类，并且必须要有&lt;br /&gt;
HandlerList这个静态常量和getHandlers这个方法&lt;br /&gt;
所以你的一个基础的自定义事件的类应该看起来像下面这样:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
因为为了不和别的事件起冲突，你必须要拥有自己的HandlerList才可以.&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件的一个小例子 ===&lt;br /&gt;
下面得到代码示范了如何快速简单的创建一个自定义的事件的类:&lt;br /&gt;
CustomEvent是类名，可以根据自己的事件类型进行更改&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 触发你的自定义事件 ====&lt;br /&gt;
你现在可以自如掌握如何创建与触发你的自定义事件了。下面是一个例子：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建你的自定义事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 现在来对你的事件做点什么&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
请您记住：你现在正在控制你的事件，如果你不去触发他，他就没有办法正常工作，什么都不会发生！&lt;br /&gt;
&lt;br /&gt;
==== 监听自定义事件 ====&lt;br /&gt;
如何来监听一个自定义事件呢？ 通常情况下，监听自定义事件与其他普通的事件是一样的！&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Making your CustomEvent Cancellable ====&lt;br /&gt;
If you ever want to make your event cancellable, remember one thing: &amp;quot;implements Cancellable.&amp;quot; Just like you would import Listener. It&#039;s really that simple, let me show you an example!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Afterwards, you would check if a plugin had cancelled the event in your code, before processing normally&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// Create the event here&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// Call the event&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// Check if the event is not cancelled&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // Now you do the event&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Video Tutorial ==&lt;br /&gt;
If you would prefer to watch a video tutorial version of this, please click [http://www.youtube.com/watch?v=PWQNsqwD-AY here].&lt;br /&gt;
&lt;br /&gt;
[[Category: 条目原文]] [[分类:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5308</id>
		<title>Event API Reference/en</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference/en&amp;diff=5308"/>
		<updated>2017-08-01T10:35:47Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 唤起你的自定义事件 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{条目原文}}&lt;br /&gt;
欢迎来到Bukkit提供的EventAPI相关教程页&lt;br /&gt;
&lt;br /&gt;
== 简介 ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;事件&#039;&#039;&#039; 就是你在使用CraftBukkit,或者基于CraftBukkit服务端的其他服务端的时候，服务端会将服务器内发生的事告诉正在监听对应事件的插件。CraftBukkit服务端为插件定义了许多不同的事件，比如玩家登录服务器，玩家进入服务器，玩家点击方块，玩家重生。还有一些可以被取消的事件例如，玩家放置方块，玩家破坏方块，等等。更还有区块卸载区块装载，实体等相关事件。具体请查看API文档:&lt;br /&gt;
BukkitAPI:https://hub.spigotmc.org/javadocs/bukkit&lt;br /&gt;
SpigotAPI:https://hub.spigotmc.org/javadocs/spigot&lt;br /&gt;
这里请注意了,有些事件是Spigot独有事件如果你想兼容CraftBukkit服务端请勿调用,例如PlayerSpawnLocationEvent&lt;br /&gt;
&lt;br /&gt;
== 开始使用EventAPI ==&lt;br /&gt;
为了让大家看得懂，这一部分先定个小目标,先只用PlayerLoginEvent做示范&lt;br /&gt;
&lt;br /&gt;
=== 设置监听指定Event的方法 ===&lt;br /&gt;
为了监听事件，我们需要创建一个新的方法，并给它加上EventHandler的注解来告诉服务端，这是个监听事件的方法,服务端就会在事件发生的时候调用该方法&lt;br /&gt;
方法名可以自己写你自己喜欢的，但传入的参数必须是你想监听的事件类&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你在监听到玩家登录时执行的代码&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
&amp;quot;@EventHandler&amp;quot; 是一个注解，用来告诉服务端这是一个监听事件的方法,你只需要把它加在你监听指定事件方法的上面就好，就像下面这样&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // EventPriority.NORMAL 是默认的监听级别&amp;lt;/source&amp;gt;&lt;br /&gt;
这样就告诉了服务端你这个监听事件的方法，优先级是普通的。&lt;br /&gt;
&lt;br /&gt;
当然你也可以自己设置事件监听的优先级比如下面这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 将该事件监听的优先级修改到HIGHEST级别&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 将该事件的优先级修改到LOW级别&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在呢你的监听事件的方法的代码应该像这样:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // Your code here...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加一个监听器 ===&lt;br /&gt;
为了监听事件，你必须让监听事件的类实现 &#039;&#039;Listener&#039;&#039; (包名是:org.bukkit.event.Listener) 这个接口, e.g.:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
上面那个名字叫&#039;&#039;onLogin&#039;&#039; 的监听玩家登录事件的方法现在什么都不能干&lt;br /&gt;
&lt;br /&gt;
就像我说的，Bukkit通过EventHandler注解来知道你这个方法是用来监听事件的，通过方法需要传入的参数知道要监听的是哪个事件，比如: &amp;lt;code&amp;gt;PlayerLoginEvent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{特别提醒:要想监听事件，你还必须得执行一个注册监听事件类的方法才能让插件正常监听事件}}&lt;br /&gt;
&lt;br /&gt;
你必须在你的插件主类里(也就是那个继承JavaPlugin的类)的onEnable方法里加上如下代码:&lt;br /&gt;
其实吧也不是非得在onEnable里面加上，在你需要监听事件的时候保证这个监听事件的类被注册过就行&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    System.out.println(event.getPlayer().getName()+&amp;quot;登入了服务器&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
这里我提醒一下各位读者&lt;br /&gt;
玩家登录服务器分四个阶段&lt;br /&gt;
HandShake(和服务器握手&lt;br /&gt;
PreLogin(预登录阶段，就是正在登录&lt;br /&gt;
Login(登入阶段,一般盗版服会直接跳过该阶段,但是是可以监听到的&lt;br /&gt;
JoinGame(成功登录服务器&lt;br /&gt;
&lt;br /&gt;
=== EventHandler注解的参数 ===&lt;br /&gt;
@EventHandler你可以使用注解来定义事件监听的相关设置&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Description&lt;br /&gt;
! Values&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| 监听事件的优先级&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| 如果你把它设置成true，如果事件被取消这个监听的事件将不再会被监听到&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
Bukkit会按照下面的顺序挨个在事件发生时通知监听该事件的插件&lt;br /&gt;
* EventPriority.LOWEST(最优先&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR(最后&lt;br /&gt;
&lt;br /&gt;
服务端将会根据这些监听器内EventHandler注解里面的监听优先级按顺序通知监听器，从LOWEST到MONITOR挨个通知插件，你可以通过设置优先级来阻止其他的插件对同等事件进行操作，并在MONITOR优先级的监听器取消后面插件对该事件的监听达到覆盖操作。&lt;br /&gt;
&lt;br /&gt;
== 注册监听事件的类 ==&lt;br /&gt;
要使你插件里包含监听器方法的类被注册，这个监听器才能起效，且包含监听方法的这个类必须实现Listener这个接口&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你只需要这一行代码来注册监听事件的类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听事件的例子 ===&lt;br /&gt;
这个监听事件的类包含两个监听器，且监听的是同一个事件，但是一个优先级是HIGH一个是默认的NORMAL&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 将监听事件的类在你的插件内起效 ===&lt;br /&gt;
下面代码示范了如何注册你的监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 直接使用插件主类作为监听类 ====&lt;br /&gt;
下面的代码示范了如何偷懒直接使用插件主类作为监听类并注册它:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 用监听类的构造器注册监听类 ===&lt;br /&gt;
有很多很多不同的方式来注册你的监听类，下面代码示范了如何使用监听类的构造器来注册监听类&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在插件的主类代码应该看起来像这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 注销你的监听器 ==&lt;br /&gt;
前面提到了如何去创建并且注册监听器使之生效，同样的服务端也提供了注销监听器的方法，你可以注销一个插件里所有的监听器，也可以注销一个插件里所有指定事件的监听器&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件内指定事件的监听器 ====&lt;br /&gt;
无一例外，所有事件都有一个静态方法:getHandlerList()，你可以使用这个方法下面的unregister()方法来对一个插件内所有的这个事件的监听器进行注销&lt;br /&gt;
比如这样搞:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// 这样就代表会注销plugin这个插件内所有监听PlayerInteractEvent这个事件的监听器&lt;br /&gt;
// 当然了你也可以单独在plugin那个参数里面写入要注销指定监听器的类&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
下面我们将会学习自定义事件&lt;br /&gt;
&lt;br /&gt;
==== 注销一个插件所有的监听器 ====&lt;br /&gt;
你可以使用HandlerList类下面的unregisterAll()这个静态方法来对一个插件所监听的所有事件的监听器进行注销&lt;br /&gt;
比如下面这样:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// 这将会注销这个插件监听这个事件的所有监听器&lt;br /&gt;
// 当然这里的参数也可以用一个指定的监听类来代替，可以单独注销一个监听器&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
要创建一个自定义的事件是很简单的，而且不会和Bukkit所自带的事件冲突&lt;br /&gt;
&lt;br /&gt;
要创建你的自定义事件，有下面的几点是需要注意的&lt;br /&gt;
你的自定义事件的类必须继承Event类，并且必须要有&lt;br /&gt;
HandlerList这个静态常量和getHandlers这个方法&lt;br /&gt;
所以你的一个基础的自定义事件的类应该看起来像下面这样:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
因为为了不和别的事件起冲突，你必须要拥有自己的HandlerList才可以.&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件的一个小例子 ===&lt;br /&gt;
下面得到代码示范了如何快速简单的创建一个自定义的事件的类:&lt;br /&gt;
CustomEvent是类名，可以根据自己的事件类型进行更改&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 触发你的自定义事件 ====&lt;br /&gt;
你现在可以自如掌握如何创建与触发你的自定义事件了。下面是一个例子：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建你的自定义事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 现在来对你的事件做点什么&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
请您记住：你现在正在控制你的事件，如果你不去触发他，他就没有办法正常工作，什么都不会发生！&lt;br /&gt;
&lt;br /&gt;
==== Listening to a Custom Event ====&lt;br /&gt;
How do you listen to a custom event you say? Simple, the same way as listening to a normal event!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Making your CustomEvent Cancellable ====&lt;br /&gt;
If you ever want to make your event cancellable, remember one thing: &amp;quot;implements Cancellable.&amp;quot; Just like you would import Listener. It&#039;s really that simple, let me show you an example!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Afterwards, you would check if a plugin had cancelled the event in your code, before processing normally&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// Create the event here&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// Call the event&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// Check if the event is not cancelled&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // Now you do the event&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Video Tutorial ==&lt;br /&gt;
If you would prefer to watch a video tutorial version of this, please click [http://www.youtube.com/watch?v=PWQNsqwD-AY here].&lt;br /&gt;
&lt;br /&gt;
[[Category: 条目原文]] [[分类:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Developing_a_permissions_plugin&amp;diff=5307</id>
		<title>Developing a permissions plugin</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Developing_a_permissions_plugin&amp;diff=5307"/>
		<updated>2017-08-01T10:32:31Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Permissible */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Developing a permissions plugin/en|点击此处查看原文]]&lt;br /&gt;
&lt;br /&gt;
== 引言  ==&lt;br /&gt;
&lt;br /&gt;
这篇教程将会带领你了解有关怎么基于新的BukkitPermissionsAPI给你插件创建权限。&lt;br /&gt;
&lt;br /&gt;
=== 前提  ===&lt;br /&gt;
&lt;br /&gt;
这篇教程需要你掌握很好的Java知识和一些插件开发知识。这篇教程只会涵盖有关Bukkit Permissions API的各种细节。&lt;br /&gt;
&lt;br /&gt;
== Permissible类  ==&lt;br /&gt;
&lt;br /&gt;
所有拥有权限集的类都必须继承自Permissible类。&amp;lt;br /&amp;gt;&lt;br /&gt;
目前这些只包含Player和CommandSender类，然而理论上任何事物都应该拥有“权限”。&amp;lt;br /&amp;gt;在接下来的教程里我们将更多的提到玩家而不是permissible对象。&lt;br /&gt;
&lt;br /&gt;
== PermissionAttachments  ==&lt;br /&gt;
&lt;br /&gt;
=== PermissionAttachment是什么?  ===&lt;br /&gt;
&amp;lt;br/&amp;gt;什么是PermissionAttachment？&lt;br /&gt;
&amp;lt;br/&amp;gt;“权限附属物”[PermissionAttachment]是插件管理玩家权限的一种手段，更胜一筹的是，它允许多插件管理玩家权限而不需要接口，除非它想要设置同样的权限。&lt;br /&gt;
&lt;br /&gt;
=== 怎样创建和移除它们  ===&lt;br /&gt;
&amp;lt;br/&amp;gt;如何创建和移除一个PermissionAttachment&lt;br /&gt;
&amp;lt;br/&amp;gt;每一位玩家必须先被“附着”在插件上，这样才能使用他们。用如下代码来创建一个PermissionAttachment:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;PermissionAttachment attachment = player.addAttachment(plugin);&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;你甚至可以创建一个仅仅只维持很短时间的attachment&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;PermissionAttachment attachment = player.addAttachment(plugin,ticks);&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;attachment需要被储存起来。最好的解决方案是用哈希表HashMap&amp;lt;UUID,PermissionAttachment&amp;gt; 来储存。用玩家的UniqueID作为键值。之后你就可以用玩家的UniqueID来管理指定玩家的权限了。 并且在不需要的时候移除它。移除attachment的地方最好是onPlayerQuit event, onPlayerKick event。并且总是在插件的onDisable中移除所有的attachment。使用下面的代码移除attachment：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;player.removeAttachment(attachment);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 配置权限 ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;这有两种主要的方法去配置玩家的权限：&lt;br /&gt;
&lt;br /&gt;
#通过字符串。这大概是最简单的方法了，所以我最先解释它。&lt;br /&gt;
#通过permission对象。这就有点复杂了，然而他内部的工作方式与第一个完全一样。所以我并不打算重温它。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
想要设置玩家的权限你必须先获取之前通过哈希表创建的PermissionAttachment。一旦你取得了PermissionAttachment，你就可以通过一种简单的方式来设置权限。代码如下：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;attachment.setPermission(permissionName,permissionValue);&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;permissionName是一个字符串，permissionValue则是布尔类型数据。之后你可以通过如下代码撤销一个玩家的权限(与将permissionValue设置成false不同)：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;attachment.unsetPermission(permissionName);&amp;lt;/source&amp;gt; &lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
你希望撤销玩家权限的原因是你希望别的权限插件来设置它。&lt;br /&gt;
== 总结  ==&lt;br /&gt;
以上就是所有想要写出自己的权限插件的你需要知道东西了。&lt;br /&gt;
一个真正的权限插件是什么样的完全取决于你。下面提供2个例子：&lt;br /&gt;
* 一个根据玩家经验值继续权限的RPG插件&lt;br /&gt;
* 一个从论坛链接权限组/权限的接口，例如从phpBB到bukkit权限&lt;br /&gt;
&lt;br /&gt;
[[Category:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=4652</id>
		<title>Event API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=4652"/>
		<updated>2017-02-25T14:03:02Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 创建自定义事件 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Event API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
服务器里发生的事情，服务器会用&#039;&#039;&#039;事件&#039;&#039;&#039; 来告诉你。 &#039;&#039;&#039;Bukkit&#039;&#039;&#039;中有各种各样的事件，例如 玩家的互动(玩家登录, 玩家点击了一个方块, 玩家死了,玩家重生...), 方块的动态(方块被放置, 方块被破坏, 方块附近的实体被改变...), 实体事件(一个怪物看上了你233, 一个苦力怕帮你收集土方块...), 世界事件 (一个世界被加载或卸载, 一个区块被加载或卸载)等等. 由于MC官方的一些原因，他们的JavaDocs都挂了= =&lt;br /&gt;
&lt;br /&gt;
== 基础 ==&lt;br /&gt;
为了保持简单易懂，这一节只讨论一种事件——PlayerLoginEnvent。让我们从创建一个监听器开始吧。&lt;br /&gt;
&lt;br /&gt;
=== 创建方法 ===&lt;br /&gt;
为了让你的插件能够监听并处理该事件的回调，你需要为它创建一个方法，这个方法就是事件&#039;&#039;&#039;监听器&#039;&#039;&#039;。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
你需要向Bukkit注册你的监听器，这样服务器才会在&amp;quot;PlayerLoginEvent&amp;quot;触发时调用该监听器。我们需要给监听器打上 @EventHandler 批注。&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
&amp;quot;@EventHandler&amp;quot; 是一个能打在某个方法上的批注，就像这样：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // 优先级 默认为NORMAL级&amp;lt;/source&amp;gt;&lt;br /&gt;
这样一来，这所对应的方法就成为了一个 优先级 为NORMAL的监听器。&lt;br /&gt;
&lt;br /&gt;
你可以设置EventHandler标签的EventPriority属性来自定义事件的优先级，例如：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 将优先级设为HIGHEST级&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 优先级设为LOW级&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你的监听器应该看上去像是这样：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加监听器 ===&lt;br /&gt;
为了让Bukkit能够注册你的监听器，你需要给你的监听器所在的类实现Listener接口(org.bukkit.event.Listener)。例如这样：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
监听器的方法名（例子中是“onlogin”的东西）并不重要；只要符合命名规则，起什么名字，你开心就好。&lt;br /&gt;
你可能想知道：“Bukkit怎么知道监听那个事件呢？”。Bukkit是通过上面例子中你定义的“PlayerLoginEvent”这个参数知道的。&lt;br /&gt;
{{warning|一个监听器只能监听一个事件，否则Bukkit是不会注册它的！}}&lt;br /&gt;
你的插件的主类 (就是继承JavaPlugin的那个类) 也可以成为一个事件监听器。在你的插件非常小的时候，这样就很方便了。比如:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    getLogger().log(Level.INFO, &amp;quot;Player &amp;quot; + event.getPlayer().getName() + &amp;quot; is logging in!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EventHandler的参数 ===&lt;br /&gt;
@EventHandler 批注可以设置某些参数来改变监听器的某些作用等。在某些情况下，你可以设置这些参数:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! 名称&lt;br /&gt;
! 类型&lt;br /&gt;
! 默认值&lt;br /&gt;
! 描述&lt;br /&gt;
! 值&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| 设置监听器优先级&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| 如果设为true，在事件被其他监听器关闭的时候，你的监听器将监听不到这个事件&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
Bukkit中的监听器拥有六个优先级&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
&lt;br /&gt;
每个插件可能会在一个事件触发时做出不同的相应，并且有的插件可能想更快的知道一个事件的发生。并且，有时候Bukkit传递事件给每个插件的时候，甚至这个事件已经被其他插件关闭了。某些情况下，其他插件刚把这个事件关掉，另外一个插件监听到了以后又把这个事件开开了。这样乱七八糟的冲突发生了以后，这就很尴尬了。&lt;br /&gt;
&lt;br /&gt;
比如 方块放置 被触发了，LOWEST优先级的监听器将会最先对这个事件做出决定，比如关闭它什么的。然后LOW优先级的监听器将会被触发，这时候可以重写LOWEST级监听器刚刚做出的修改，以此类推。Eventually it hits monitor, and at this point nothing should change the outcome of the event. MONITOR级的监听器 should be used to see the outcome of an event, without changing any aspect of it.&lt;br /&gt;
If we have three plugins enabled; one is a basic area protection plugin, one is a fancy plugin using signs, and another is a logging plugin.&lt;br /&gt;
The protection plugin listens on Priority.LOWEST. It says they can&#039;t place blocks in this area, and cancels the event.&lt;br /&gt;
The fancy sign plugin listens on Priority.NORMAL. It says they can place signs here, and uncancels the event.&lt;br /&gt;
The log plugin listens on Priority.MONITOR. It sees that the event was actually allowed, and logs it.&lt;br /&gt;
&lt;br /&gt;
If you want to change the outcome of an event, choose very carefully from LOWEST to HIGHEST. Suggested generalized protection plugins on lowest, more specific plugins on normal, and override plugins on high.&lt;br /&gt;
If you want to act when an event happens, but not change the outcome, use MONITOR. &#039;&#039;&#039;It&#039;s really really important that you use MONITOR, or an event might get cancelled after you&#039;ve acted on it, and it&#039;s even more important that you don&#039;t change the outcome of the event on MONITOR or it&#039;ll break other plugins.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;中文简单解释&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
优先级越高的方法越早调用&lt;br /&gt;
&lt;br /&gt;
优先级再高(除了MONITOR) setCanceled() 方法也不能阻止其它插件监听到它&lt;br /&gt;
&lt;br /&gt;
在MONITOR上搞 setCanceled(false) 其它插件会给你玩死~&lt;br /&gt;
&lt;br /&gt;
== 注册事件(监听器) ==&lt;br /&gt;
要注册你的方法，包含EventHandler的类必须是implent了Listenr类的&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
你只需要提供一个插件和一个监听器来在PluginManager里注册它们&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听器示例 ===&lt;br /&gt;
这个监听器包含两个EventHandlers，一个监听级别是HIGH并且另一个是NORMAL。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在插件中注册事件监听器 ===&lt;br /&gt;
The registerEvents method requires a listener and a plugin. Luckily, we already have our LoginListener. Now for the LoginPlugin!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 在实现监听器接口的插件中注册事件监听器 ====&lt;br /&gt;
&amp;lt;ref&amp;gt;准确来说是在实现了监听器接口的继承了JavaPlugin的类中注册事件（笑&amp;lt;/ref&amp;gt;&lt;br /&gt;
You could even have the events in the main class, for example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在监听器中注册事件监听器 ===&lt;br /&gt;
There are many ways to register your events. Here&#039;s an example where you register them in your listener class.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The LoginPlugin would look like this:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 在监听器中注销事件监听器 ==&lt;br /&gt;
You can un-register individual events, entire listener classes or all events registered by your plugin or even by other plugins!&lt;br /&gt;
&lt;br /&gt;
==== 注销特定的事件监听器 ====&lt;br /&gt;
Each event class has the getHandlerList() static method, call that and then you can use .unregister() method.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// this will unregister all PlayerInteractEvent instances from the plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Now you know why you&#039;ll need the getHandlerList() in your custom events.&lt;br /&gt;
&lt;br /&gt;
==== 注销所有事件监听器 ====&lt;br /&gt;
Using the HandlerList class and its unregisterAll() static method you can easily unregister events from listener classes or plugins.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// this will unregister all events from the specified plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
创建一个自定义事件是非常简单的，你可以使用跟Bukkit用的一样的系统，而不需要担心服务器性能的问题。&lt;br /&gt;
&lt;br /&gt;
在你创建一个自定义事件的时候，有两个东西你要记住——&amp;quot;继承 Event 类&amp;quot; 和 &amp;quot;静态的 handlers&amp;quot; 。你必须像下面这样写来做静态的handlers：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
这块代码可以让 EventHandlers 更方便的操作事件，使无关的事件完全的分开。&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件示例 ===&lt;br /&gt;
下面的这个例子向你展示如何简单地创建一个你自己的事件——“CustomEvent事件”。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 调用你的自定义事件 ====&lt;br /&gt;
You are in control of creating and calling your events, where you call it is completely up to you. Here&#039;s an example&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建一个自定义事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件（创建好自定义事件对象，要使用本语句触发事件，否则什么都不会发生）&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 然后继续操作事件&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
请你记住！你正在控制你的自定义事件的对象。如果你不触发它，那就啥都不会发生。&lt;br /&gt;
&lt;br /&gt;
==== 监听一个自定义事件 ====&lt;br /&gt;
如何监听一个自定义事件呢？简单！就像监听Bukkit其他普通的事件是一样的！&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // 你的代码&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 使得你的自定义事件具有可撤销性 ====&lt;br /&gt;
如果你想让你的自定义事件可以被撤销，记得，要“继承Cancellable类”。就像你继承Listener那样。这十分简单，让我向你展示一个例子！&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
之后，你需要在你执行相应代码前，检查一下是否有插件把你的事件撤销掉了。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建一个事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 检查一下是否事件被撤销&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // 现在在这里做些什么吧！&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 视频教程 ==&lt;br /&gt;
If you would prefer to watch a video tutorial version of this, please click [http://www.youtube.com/watch?v=PWQNsqwD-AY here].&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
[[Category: Tutorials]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=4651</id>
		<title>Event API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=4651"/>
		<updated>2017-02-25T13:41:35Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 使得你的自定义事件具有可撤销性 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Event API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
服务器里发生的事情，服务器会用&#039;&#039;&#039;事件&#039;&#039;&#039; 来告诉你。 &#039;&#039;&#039;Bukkit&#039;&#039;&#039;中有各种各样的事件，例如 玩家的互动(玩家登录, 玩家点击了一个方块, 玩家死了,玩家重生...), 方块的动态(方块被放置, 方块被破坏, 方块附近的实体被改变...), 实体事件(一个怪物看上了你233, 一个苦力怕帮你收集土方块...), 世界事件 (一个世界被加载或卸载, 一个区块被加载或卸载)等等. 由于MC官方的一些原因，他们的JavaDocs都挂了= =&lt;br /&gt;
&lt;br /&gt;
== 基础 ==&lt;br /&gt;
为了保持简单易懂，这一节只讨论一种事件——PlayerLoginEnvent。让我们从创建一个监听器开始吧。&lt;br /&gt;
&lt;br /&gt;
=== 创建方法 ===&lt;br /&gt;
为了让你的插件能够监听并处理该事件的回调，你需要为它创建一个方法，这个方法就是事件&#039;&#039;&#039;监听器&#039;&#039;&#039;。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
你需要向Bukkit注册你的监听器，这样服务器才会在&amp;quot;PlayerLoginEvent&amp;quot;触发时调用该监听器。我们需要给监听器打上 @EventHandler 批注。&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
&amp;quot;@EventHandler&amp;quot; 是一个能打在某个方法上的批注，就像这样：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // 优先级 默认为NORMAL级&amp;lt;/source&amp;gt;&lt;br /&gt;
这样一来，这所对应的方法就成为了一个 优先级 为NORMAL的监听器。&lt;br /&gt;
&lt;br /&gt;
你可以设置EventHandler标签的EventPriority属性来自定义事件的优先级，例如：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 将优先级设为HIGHEST级&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 优先级设为LOW级&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你的监听器应该看上去像是这样：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加监听器 ===&lt;br /&gt;
为了让Bukkit能够注册你的监听器，你需要给你的监听器所在的类实现Listener接口(org.bukkit.event.Listener)。例如这样：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
监听器的方法名（例子中是“onlogin”的东西）并不重要；只要符合命名规则，起什么名字，你开心就好。&lt;br /&gt;
你可能想知道：“Bukkit怎么知道监听那个事件呢？”。Bukkit是通过上面例子中你定义的“PlayerLoginEvent”这个参数知道的。&lt;br /&gt;
{{warning|一个监听器只能监听一个事件，否则Bukkit是不会注册它的！}}&lt;br /&gt;
你的插件的主类 (就是继承JavaPlugin的那个类) 也可以成为一个事件监听器。在你的插件非常小的时候，这样就很方便了。比如:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    getLogger().log(Level.INFO, &amp;quot;Player &amp;quot; + event.getPlayer().getName() + &amp;quot; is logging in!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EventHandler的参数 ===&lt;br /&gt;
@EventHandler 批注可以设置某些参数来改变监听器的某些作用等。在某些情况下，你可以设置这些参数:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! 名称&lt;br /&gt;
! 类型&lt;br /&gt;
! 默认值&lt;br /&gt;
! 描述&lt;br /&gt;
! 值&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| 设置监听器优先级&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| 如果设为true，在事件被其他监听器关闭的时候，你的监听器将监听不到这个事件&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
Bukkit中的监听器拥有六个优先级&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
&lt;br /&gt;
每个插件可能会在一个事件触发时做出不同的相应，并且有的插件可能想更快的知道一个事件的发生。并且，有时候Bukkit传递事件给每个插件的时候，甚至这个事件已经被其他插件关闭了。某些情况下，其他插件刚把这个事件关掉，另外一个插件监听到了以后又把这个事件开开了。这样乱七八糟的冲突发生了以后，这就很尴尬了。&lt;br /&gt;
&lt;br /&gt;
比如 方块放置 被触发了，LOWEST优先级的监听器将会最先对这个事件做出决定，比如关闭它什么的。然后LOW优先级的监听器将会被触发，这时候可以重写LOWEST级监听器刚刚做出的修改，以此类推。Eventually it hits monitor, and at this point nothing should change the outcome of the event. MONITOR级的监听器 should be used to see the outcome of an event, without changing any aspect of it.&lt;br /&gt;
If we have three plugins enabled; one is a basic area protection plugin, one is a fancy plugin using signs, and another is a logging plugin.&lt;br /&gt;
The protection plugin listens on Priority.LOWEST. It says they can&#039;t place blocks in this area, and cancels the event.&lt;br /&gt;
The fancy sign plugin listens on Priority.NORMAL. It says they can place signs here, and uncancels the event.&lt;br /&gt;
The log plugin listens on Priority.MONITOR. It sees that the event was actually allowed, and logs it.&lt;br /&gt;
&lt;br /&gt;
If you want to change the outcome of an event, choose very carefully from LOWEST to HIGHEST. Suggested generalized protection plugins on lowest, more specific plugins on normal, and override plugins on high.&lt;br /&gt;
If you want to act when an event happens, but not change the outcome, use MONITOR. &#039;&#039;&#039;It&#039;s really really important that you use MONITOR, or an event might get cancelled after you&#039;ve acted on it, and it&#039;s even more important that you don&#039;t change the outcome of the event on MONITOR or it&#039;ll break other plugins.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;中文简单解释&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
优先级越高的方法越早调用&lt;br /&gt;
&lt;br /&gt;
优先级再高(除了MONITOR) setCanceled() 方法也不能阻止其它插件监听到它&lt;br /&gt;
&lt;br /&gt;
在MONITOR上搞 setCanceled(false) 其它插件会给你玩死~&lt;br /&gt;
&lt;br /&gt;
== 注册事件(监听器) ==&lt;br /&gt;
要注册你的方法，包含EventHandler的类必须是implent了Listenr类的&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
你只需要提供一个插件和一个监听器来在PluginManager里注册它们&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听器示例 ===&lt;br /&gt;
这个监听器包含两个EventHandlers，一个监听级别是HIGH并且另一个是NORMAL。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在插件中注册事件监听器 ===&lt;br /&gt;
The registerEvents method requires a listener and a plugin. Luckily, we already have our LoginListener. Now for the LoginPlugin!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 在实现监听器接口的插件中注册事件监听器 ====&lt;br /&gt;
&amp;lt;ref&amp;gt;准确来说是在实现了监听器接口的继承了JavaPlugin的类中注册事件（笑&amp;lt;/ref&amp;gt;&lt;br /&gt;
You could even have the events in the main class, for example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在监听器中注册事件监听器 ===&lt;br /&gt;
There are many ways to register your events. Here&#039;s an example where you register them in your listener class.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The LoginPlugin would look like this:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 在监听器中注销事件监听器 ==&lt;br /&gt;
You can un-register individual events, entire listener classes or all events registered by your plugin or even by other plugins!&lt;br /&gt;
&lt;br /&gt;
==== 注销特定的事件监听器 ====&lt;br /&gt;
Each event class has the getHandlerList() static method, call that and then you can use .unregister() method.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// this will unregister all PlayerInteractEvent instances from the plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Now you know why you&#039;ll need the getHandlerList() in your custom events.&lt;br /&gt;
&lt;br /&gt;
==== 注销所有事件监听器 ====&lt;br /&gt;
Using the HandlerList class and its unregisterAll() static method you can easily unregister events from listener classes or plugins.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// this will unregister all events from the specified plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
Creating custom events is very simple, you can use the same system that Bukkit uses without ruining performance.&lt;br /&gt;
&lt;br /&gt;
There are two (2) things to keep in mind when you create a Custom Event. They are &amp;quot;extend Event&amp;quot; and &amp;quot;static handlers.&amp;quot; With static handlers, you must input the following code into your custom event:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This block of code makes the EventHandlers contained inside your own event, keeping any unrelated events completely separated.&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件示例 ===&lt;br /&gt;
The following example shows how easy it is to create your own &amp;quot;CustomEvent.&amp;quot;&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 调用你的自定义事件 ====&lt;br /&gt;
You are in control of creating and calling your events, where you call it is completely up to you. Here&#039;s an example&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// Create the event here&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// Call the event&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// Now you do the event&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember: You are in control of your events. If you don&#039;t call it, and act upon it, it doesn&#039;t happen!&lt;br /&gt;
&lt;br /&gt;
==== 监听一个自定义事件 ====&lt;br /&gt;
How do you listen to a custom event you say? Simple, the same way as listening to a normal event!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 使得你的自定义事件具有可撤销性 ====&lt;br /&gt;
如果你想让你的自定义事件可以被撤销，记得，要“继承Cancellable类”。就像你继承Listener那样。这十分简单，让我向你展示一个例子！&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
之后，你需要在你执行相应代码前，检查一下是否有插件把你的事件撤销掉了。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// 创建一个事件对象&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// 触发事件&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// 检查一下是否事件被撤销&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // 现在在这里做些什么吧！&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 视频教程 ==&lt;br /&gt;
If you would prefer to watch a video tutorial version of this, please click [http://www.youtube.com/watch?v=PWQNsqwD-AY here].&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
[[Category: Tutorials]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=3496</id>
		<title>Bukkit/插件开发教程</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=3496"/>
		<updated>2016-08-19T08:04:39Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{待完善}}&lt;br /&gt;
{{待更新|url=http://wiki.bukkit.org/Plugin_Tutorial}}&lt;br /&gt;
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]&lt;br /&gt;
&lt;br /&gt;
== 介绍  ==&lt;br /&gt;
&lt;br /&gt;
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.&lt;br /&gt;
&lt;br /&gt;
== 学习Java  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;理解这些教程需要具备Java这门编程语言的基本知识.&#039;&#039;&#039;如果你刚开始学习Java或者需要重温一下相关知识，下面有一个并不完整的网站列表可供参考.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;[http://docs.oracle.com/javase/tutorial/ Oracle&#039;s Java Tutorials]&amp;lt;/big&amp;gt; - 官方教程&lt;br /&gt;
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程&lt;br /&gt;
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程&lt;br /&gt;
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书&lt;br /&gt;
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.&lt;br /&gt;
*[http://www.yiibai.com/java/java_quick_guide.html JavaQuickGuide] - 易百快速入门教程&lt;br /&gt;
&lt;br /&gt;
https://docs.oracle.com/javase/8/docs/api/     JavaSE爪哇文挡&lt;br /&gt;
&lt;br /&gt;
== 开发环境  ==&lt;br /&gt;
&lt;br /&gt;
在编写一个插件前 (或学习Java前) 你需要配置一个开发者环境，这个包括但不限于安装IDE (Integrated Development Environment，集成开发环境)。接下来的教程中有关于Eclipse上IDE的操作指南。&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;更多信息，请参见 [[Bukkit/插件开发教程-建立工作区|建立工作区]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
你&#039;&#039;&#039;必须&#039;&#039;&#039;下载&#039;&#039;&#039;Java&#039;&#039;&#039;开发者使用的Eclipse构建版本, &#039;&#039;&#039;不是&#039;&#039;&#039; &#039;&#039;&#039;Java EE&#039;&#039;&#039;开发者的版本。 Java EE 开发者的版本不包含此教程需要的Maven支持。&lt;br /&gt;
&lt;br /&gt;
或者使用更加方便快捷的NetBeansIDE编写插件&lt;br /&gt;
&lt;br /&gt;
[[文件:NetBeanIDE-FristSee.jpg]]&lt;br /&gt;
&lt;br /&gt;
下载地址：&lt;br /&gt;
&lt;br /&gt;
https://netbeans.org/downloads/index.html&lt;br /&gt;
&lt;br /&gt;
下载指定的安装包&lt;br /&gt;
&lt;br /&gt;
[[文件:NetBeanIDE-download.jpg]]&lt;br /&gt;
&lt;br /&gt;
当然安装前需要JDK，下载网站：  &lt;br /&gt;
&lt;br /&gt;
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html    &lt;br /&gt;
&lt;br /&gt;
打开安装程序后点击&lt;br /&gt;
 下一步 &amp;gt; 我接受许可证协议中的条款 &amp;gt; 下一步&lt;br /&gt;
 要不要Juit你来定 &amp;gt; 下一步 &amp;gt; 安装&lt;br /&gt;
&lt;br /&gt;
== 了解BukkitAPI和学看它的Javadocs ==&lt;br /&gt;
&lt;br /&gt;
=== 结构与Java文档 ===&lt;br /&gt;
&amp;lt;big&amp;gt;工欲善其事，必先利其器&amp;lt;/big&amp;gt; 我们做的是Bukkit插件，当然要了解它了&lt;br /&gt;
&lt;br /&gt;
这是给菜鸟看的，高手自觉跳过&lt;br /&gt;
&lt;br /&gt;
要扯到Javadocs上，关于这货的属性以后会详细解释&lt;br /&gt;
&lt;br /&gt;
 Javadocs 是一种关于程序的帮助文档，帮助开发者更了解程序的构造&lt;br /&gt;
&lt;br /&gt;
BukkitAPI当然有Javadocs ，在 https://hub.spigotmc.org/javadocs/bukkit/ 上有&lt;br /&gt;
&lt;br /&gt;
全景图&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-Javadocs-1.jpg]]&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-Javadocs-2.jpg]]&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-Javadocs-3.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
解压一个服务端(Craftbukkit/Spigot)查看&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-see-1.jpg]]&lt;br /&gt;
&lt;br /&gt;
进入&amp;lt;big&amp;gt;org&amp;lt;/big&amp;gt;文件夹 (在Java中这叫&amp;lt;big&amp;gt;包&amp;lt;/big&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-see-2.jpg]]&lt;br /&gt;
&lt;br /&gt;
看，BukkitAPI就在这里，这是一个Spigotmc服务端，所以也可以看到spigotAPI&lt;br /&gt;
&lt;br /&gt;
Spigotmc是依靠BukkitAPI的，所以spigotAPI分量很轻(自己动手去看就知道了)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-see-3.jpg]]&lt;br /&gt;
&lt;br /&gt;
橙色框内的就是包，红框(及以下)是编译好的class文件&lt;br /&gt;
&lt;br /&gt;
和全景图连起来看看，是不是一一对应。&lt;br /&gt;
&lt;br /&gt;
额...那个...说好的&amp;lt;big&amp;gt;class&amp;lt;/big&amp;gt;呢？&lt;br /&gt;
&lt;br /&gt;
点击Javadocs的 org.bukkit 超链接&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-Javadocs-4.jpg]]&lt;br /&gt;
&lt;br /&gt;
就在这里&lt;br /&gt;
&lt;br /&gt;
全景图拍摄的是&amp;lt;big&amp;gt;Overview&amp;lt;/big&amp;gt; ，它显示的只是包和它的分支而已&lt;br /&gt;
&lt;br /&gt;
拿 org.bukkit.block 和 org.bukkit.block.bander 举例&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;block&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-Javadocs-5.jpg]]&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-see-4.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;block.bannder&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-Javadocs-6.jpg]]&lt;br /&gt;
&lt;br /&gt;
[[文件:BukkitAPI-see-5.jpg]]&lt;br /&gt;
&lt;br /&gt;
一般情况下，许多人说的 &amp;lt;big&amp;gt;去看API&amp;lt;/big&amp;gt; 实际意思是看BukkitAPI的Javadocs&lt;br /&gt;
&lt;br /&gt;
=== other ===&lt;br /&gt;
&lt;br /&gt;
 building …&lt;br /&gt;
&lt;br /&gt;
== 新建插件开发项目  ==&lt;br /&gt;
&lt;br /&gt;
=== 创建一个新的项目 ===&lt;br /&gt;
&lt;br /&gt;
==== Eclipse ====&lt;br /&gt;
&lt;br /&gt;
在开始工作之前，你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击&#039;&#039;File -&amp;gt; New -&amp;gt; Project:&#039;&#039;来创建一个新的项目.&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject.png]] &lt;br /&gt;
&lt;br /&gt;
现在,打开&#039;&#039;Maven&#039;&#039;文件夹, 然后选择&#039;&#039;Maven Project&#039;&#039;.点击next,之后在下一个菜单中选择&#039;&#039;Create a simple project&#039;&#039;, 再次点击&#039;&#039;Next&#039;&#039;:&lt;br /&gt;
如果你看不到&#039;&#039;Maven&#039;&#039;文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject2.png]] &lt;br /&gt;
&lt;br /&gt;
现在，你需要给你的组用户命名，就像下面这样:&lt;br /&gt;
* 如果你拥有一个域名，package则填写逆转的域名地址. &lt;br /&gt;
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]&lt;br /&gt;
** 避免使用一个不属于你自己的域名.&lt;br /&gt;
* 没有域名? 这里有几种方法可供选择。&lt;br /&gt;
*# 在资源管理站点创建一个用户，比如GitHub或是sourceforge&lt;br /&gt;
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 使用你的邮箱.  例如: &amp;lt;username&amp;gt;@gmail.com格式的邮箱应输入com.gmail.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 最不济的方法.：  使用独特的组名命名方式，这应当是你最后的解决方法。&lt;br /&gt;
&lt;br /&gt;
以下几个地址不能作为package中的地址前缀:&lt;br /&gt;
* org.bukkit&lt;br /&gt;
* net.bukkit&lt;br /&gt;
* com.bukkit&lt;br /&gt;
* net.minecraft&lt;br /&gt;
&lt;br /&gt;
完成基础的组名以后，你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 &#039;&#039;TestPlugin&#039;&#039;的插件，那么完整的组名是&#039;&#039;io.github.&amp;lt;username&amp;gt;&#039;&#039;, 你的工程名也是 &#039;&#039;TestPlugin&#039;&#039;. 至于版本，默认即可，稍后可以修改。&lt;br /&gt;
完成向导:&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject3.png]] &lt;br /&gt;
&lt;br /&gt;
如果这是你首次使用Eclipse, 点击右上角的&amp;quot;X&amp;quot; 来关闭Welcome提示页面. 现在，你的窗口视图看起来就像下面的图片这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Eclipsemain.png]] &lt;br /&gt;
&lt;br /&gt;
点击工程名称右边的箭头来进行下一步，现在我们正式开始。&lt;br /&gt;
&lt;br /&gt;
==== Netbeans ====&lt;br /&gt;
&lt;br /&gt;
先创建项目&lt;br /&gt;
&lt;br /&gt;
[[文件:Nb1.jpg]]&lt;br /&gt;
&lt;br /&gt;
[[文件:TNb2.jpg]]&lt;br /&gt;
&lt;br /&gt;
接下来更改主类中的内容&lt;br /&gt;
&lt;br /&gt;
[[文件:Nb3.jpg]]&lt;br /&gt;
&lt;br /&gt;
当一个类继承Javaplugin后 ，它就是插件的主类&lt;br /&gt;
&lt;br /&gt;
不允许其他类继承主类。&lt;br /&gt;
&lt;br /&gt;
=== 添加Bukkit API ===&lt;br /&gt;
&lt;br /&gt;
==== Eclipse ====&lt;br /&gt;
&lt;br /&gt;
在开发插件之前，你需要添加 Bukkit API库文件到你的项目，作为一个dependency, 你也可以添加其他你想实用的API.&lt;br /&gt;
&lt;br /&gt;
找到项目文件夹中部的&#039;&#039;pom.xml&#039;&#039;并双击进行编辑. 点击&#039;&#039;pom.xml&#039;&#039;中部的 tab,你将会看到下图所示内容:&lt;br /&gt;
&lt;br /&gt;
[[Image:Pomeditor.png]]&lt;br /&gt;
&lt;br /&gt;
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;build&amp;gt;&lt;br /&gt;
       &amp;lt;plugins&amp;gt;&lt;br /&gt;
           &amp;lt;plugin&amp;gt;&lt;br /&gt;
               &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;
               &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
               &amp;lt;configuration&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;amp;lt;&amp;lt;/nowiki&amp;gt;source&amp;gt;1.7&amp;lt;/source&amp;gt;&lt;br /&gt;
                   &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;&lt;br /&gt;
               &amp;lt;/configuration&amp;gt;&lt;br /&gt;
           &amp;lt;/plugin&amp;gt;&lt;br /&gt;
       &amp;lt;/plugins&amp;gt;&lt;br /&gt;
    &amp;lt;/build&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你或许想使用其他版本,例如 &amp;lt;code&amp;gt;1.8&amp;lt;/code&amp;gt; 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码&amp;quot;error&amp;quot;中建议你更改语言版本.同意即可.&lt;br /&gt;
&lt;br /&gt;
在位于代码中段的&#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit&#039;s repository的地址): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;repositories&amp;gt;&lt;br /&gt;
        &amp;lt;repository&amp;gt;&lt;br /&gt;
            &amp;lt;id&amp;gt;bukkit-repo&amp;lt;/id&amp;gt;&lt;br /&gt;
            &amp;lt;url&amp;gt;http://repo.bukkit.org/content/groups/public/&amp;lt;/url&amp;gt;&lt;br /&gt;
        &amp;lt;/repository&amp;gt;&lt;br /&gt;
    &amp;lt;/repositories&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后继续在 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;dependencies&amp;gt;&lt;br /&gt;
        &amp;lt;dependency&amp;gt;&lt;br /&gt;
            &amp;lt;groupId&amp;gt;org.bukkit&amp;lt;/groupId&amp;gt;&lt;br /&gt;
            &amp;lt;artifactId&amp;gt;bukkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
            &amp;lt;version&amp;gt;1.7.2-R0.3&amp;lt;/version&amp;gt;&lt;br /&gt;
            &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;
            &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;
        &amp;lt;/dependency&amp;gt;&lt;br /&gt;
    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.&lt;br /&gt;
&lt;br /&gt;
当你完成上述步骤后,&#039;&#039;pom.xml&#039;&#039;中的内容应该是这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Finishedpom.png]]&lt;br /&gt;
&lt;br /&gt;
通过菜单 &#039;&#039;File -&amp;gt; Save&#039;&#039;或者按住 &amp;lt;code&amp;gt;Ctrl + S&amp;lt;/code&amp;gt; 来保存&#039;&#039;pom.xml&#039;&#039;文件 .  之后, 右键项目标题，然后依次选择 &#039;&#039;Maven -&amp;gt; Update Project&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== NetBeans ====&lt;br /&gt;
&lt;br /&gt;
这个相对简单，先创建库&lt;br /&gt;
&lt;br /&gt;
[[文件:NetBeansIDE-lib.jpg]] &lt;br /&gt;
&lt;br /&gt;
[[文件:NetBeansIDE-lib-setin.jpg]] &lt;br /&gt;
&lt;br /&gt;
然后添加&amp;lt;库类路径&amp;gt;，一般是服务端核心文件&lt;br /&gt;
&lt;br /&gt;
[[文件:NetBeansIDE-lib-use.jpg]]&lt;br /&gt;
&lt;br /&gt;
由此完成设置&lt;br /&gt;
&lt;br /&gt;
=== Bukkit Java文档 ===&lt;br /&gt;
&lt;br /&gt;
 如果你曾经使用过Eclipse和Java，你会知道当你将鼠标准心移至class或者 method中的代码部分时，一个含有相关文档内容的黄色小框会弹出来. 这就是（Java的）Java文档。&lt;br /&gt;
 &lt;br /&gt;
 你可以[http://download.oracle.com/javase/6/docs/api/ Oracle website 在线获取]它.。Bukkit同样拥有文档，这些文档内容包含了关于API中method和class的有用描述。&lt;br /&gt;
 &lt;br /&gt;
 你可以在[http://jd.bukkit.org/ 这]找到它 (Beta版的Java文档在[http://jd.bukkit.org/ 这]找, 新开发出来的Java文档在  &lt;br /&gt;
 &lt;br /&gt;
 [http://jd.bukkit.org/ 这]) 。为了在Eclipse里使用Java文档,这样的话当你的鼠标停留在Bukkit的class和method上时，&lt;br /&gt;
 &lt;br /&gt;
 （Bukkit的）Java文档就会冒出来。  &lt;br /&gt;
JavaDoc in spigot [https://hub.spigotmc.org/javadocs/bukkit/ Click Here]&lt;br /&gt;
&lt;br /&gt;
 首先右键在侧边栏里的&amp;quot;Maven Dependencies&amp;quot;下面的Bukkit jar,选择&amp;quot;Properties&amp;quot;.选中窗口左边的&#039;&#039;Javadoc Location&#039;&#039;, 并粘贴如下网址 &lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;http://jd.bukkit.org/&#039;&#039;&#039; (或者上面的测试版/最新版 Java文档链接也好) 到&amp;quot;Javadoc URL&amp;quot;下面的输入框内. 就像这样: &lt;br /&gt;
&lt;br /&gt;
[[Image:Bukkitjavadocs.png]] &lt;br /&gt;
&lt;br /&gt;
 点击 &amp;quot;Validate&amp;quot;,然后再点击&amp;quot;OK&amp;quot;就完成了.现在Bukkit Javadocs已经连接到Bukkit提供的资源,同时你也可以通过Eclipse来获得帮助类文档信息了.&lt;br /&gt;
&lt;br /&gt;
=== 创建一个包 ===&lt;br /&gt;
&lt;br /&gt;
 现在你需要创建一个包，它将储存所有我们需要使用的Java类文件. 右键展开&#039;&#039;src/main/java&#039;&#039;折叠并选择 &#039;&#039;New &amp;amp;gt; Package&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
[[Image:Newpackage.png]] &lt;br /&gt;
&lt;br /&gt;
 包名的话，先写上你的组名，然后加个点，再写上你的小写Artifact ID。举例, 如果你的组名是&#039;&#039;io.github.name&#039;&#039;，你的artifact名是 &#039;&#039;TestPlugin&#039;&#039;。&lt;br /&gt;
 &lt;br /&gt;
 你的包名就应该是&#039;&#039;io.github.name.testplugin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== 创建插件的类文件 ===&lt;br /&gt;
&lt;br /&gt;
既然我们已经建立好了我们的项目，我们接下来可以开始添加类文件以及制作插件了. 插件的主类指的是拓展(extends)JavaPlugin的类文件. 在你的插件中只能有一个类文件拓展JavaPlugin，无论直接还是间接. 先创建你的主类文件并保持名称与插件名一致是个很好的习惯.右键你之前新建的包,并选择&amp;amp;nbsp; &#039;&#039;New &amp;amp;gt; Class&#039;&#039;.你将会创建一个新的类文件，就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$GroupName}.{$ArtifactName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$ArtifactName} extends JavaPlugin {&lt;br /&gt;
    &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
警告： 插件绝对不应该调用自己的构造函数并实例化&lt;br /&gt;
&lt;br /&gt;
=== 创建plugin.yml ===&lt;br /&gt;
&lt;br /&gt;
你已经创建了项目和主类，如果你想要Bukkit能够加载你的插件，你还必须创建 &#039;&#039;&#039;[[Plugin YAML|plugin.yml]]&#039;&#039;&#039; 文件. 这个文件包含有基础的插件信息，如果缺失这个文件，你的插件也将会失效. 此时你需要右键&#039;&#039;src/main/resources&#039;&#039;. 选择&#039;&#039;New &amp;amp;gt; File&#039;&#039;. 命名为&amp;quot;&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;&amp;quot; 并右键完成新文件的创建.Eclipse会打开默认的文本编辑窗口来显示&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;文件中空白的内容并提供编辑. &#039;&#039;(Hint:&amp;amp;nbsp;如果你想要使得你的工作空间更加规整，关闭文本编辑器并将&#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件拖到主工作空间(拖到文件目录的右边) 之后你就可以在eclipse中编辑文件了.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
有三个基础的内容需要在plugin.yml写明. &lt;br /&gt;
: name:  插件名称. &lt;br /&gt;
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 插件主类的完整，合法名称] . &lt;br /&gt;
: version: 插件当前版本号. &lt;br /&gt;
&lt;br /&gt;
最简单的 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件内就像这样&amp;amp;nbsp;: &lt;br /&gt;
&lt;br /&gt;
 name: {插件名称}&lt;br /&gt;
 main: {包名}.{主类}&lt;br /&gt;
 version: {版本号}&lt;br /&gt;
&lt;br /&gt;
 PS:插件的包名经常会包括插件的名称，所以看到这个的时候不要感到惊讶。&lt;br /&gt;
    你的主类是否是你的插件名取决于你之前的命名方式，时刻记住这一点很重要。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;更多例子&#039;&#039;&#039;, 请看[[#Example_Files_and_Templates]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
此时你的插件已经可以被Bukkit加载了,同时服务端日志也会开始记录你的插件. 不过它现在什么用都没有!&lt;br /&gt;
&lt;br /&gt;
== onEnable()和onDisable()方法  ==&lt;br /&gt;
&lt;br /&gt;
这些方法将在插件启用与卸载时生效. 默认情况下，你的插件在被加载时会调用这些方法，所以你可以在这里注册你需要用到的事件和提供一些调试信息. &amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 方法会在插件启用时被调用, 需要包含初始化插件的逻辑语句. &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法会在插件卸载时被调用，需要包含清理(clean up)插件的逻辑语句和连接声明(associated state)。此外，插件可以重写 &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; 方法来在插件载入时执行附加的逻辑语句。&lt;br /&gt;
&lt;br /&gt;
=== onEnable()和onDisable()方法介绍  ===&lt;br /&gt;
&lt;br /&gt;
在前面的章节中，我们在主类创建了&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法. 此时，这些代码看起来就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
     @Override&lt;br /&gt;
     public void onEnable() {&lt;br /&gt;
         // 插件载入时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     @Override&lt;br /&gt;
     public void onDisable() {&lt;br /&gt;
         // 插件卸载时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这些方法已经创建，但目前它们还没有任何作用.&lt;br /&gt;
注意: 不需要添加代码来实现输出信息&amp;quot;{$PluginName} has been enabled!&amp;quot; ，因为bukkit会自动输出此类信息&lt;br /&gt;
有关更多事件的信息请查看 [[Event_API_Reference|这里]].&lt;br /&gt;
&lt;br /&gt;
=== 发送提示信息 ===&lt;br /&gt;
&lt;br /&gt;
 插件能够输出信息至控制台与服务器日志。这是通过调用插件记录器里正确的方法来实现的。首先我们必须调用 &amp;lt;code&amp;gt;getLogger()&amp;lt;/code&amp;gt; 方法来检索该插件的记录器，&lt;br /&gt;
 &lt;br /&gt;
 然后我们就可以开始记录信息了。&lt;br /&gt;
&lt;br /&gt;
我们通过调用&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法可以实现输出信息。 我们需要在&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法下面插入如下代码：&lt;br /&gt;
&lt;br /&gt;
 getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
在 &amp;lt;code&amp;gt;&#039;&#039;&#039;onDisable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法里面，你也可以做类似的事情，不过要改改输出信息。&lt;br /&gt;
&lt;br /&gt;
此时你的主类文件就像这样: &lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
         @Override&lt;br /&gt;
         public void onEnable() {&lt;br /&gt;
             getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public void onDisable() {&lt;br /&gt;
         getLogger().info(&amp;quot;onDisable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 防止重载后插件失效 ===&lt;br /&gt;
&lt;br /&gt;
你需要知道的是，插件并不只会伴随服务器关闭、启动而重载，你的插件也可能会被其他插件启用、关闭，或是在服务器运行时使用/reload命令. &lt;br /&gt;
假设服务器启动之后一会儿后，你的插件才被启用，这时便是危险的,因为服务器中可能已经有玩家在线、额外的世界被加载以及很多预想不到的不同之处(与&amp;quot;插件同服务器同时加载&amp;quot;的情况相比较). &lt;br /&gt;
&lt;br /&gt;
举个例子来说:&lt;br /&gt;
* 你有一个用来储存HashMap中的玩家登入信息的插件&lt;br /&gt;
* 你希望该信息对所有玩家都可见&lt;br /&gt;
* 一名管理员使用/reload命令&lt;br /&gt;
* 你的插件被卸载，所有储存的数据都丢失了&lt;br /&gt;
* 你的插件被再次启用时，一些玩家已经在线&lt;br /&gt;
* 这些玩家在HashMap并没有储存的信息&lt;br /&gt;
* 你试图重新获得他们的信息，但是失败了!&lt;br /&gt;
&lt;br /&gt;
为了插件重载以后能够正常工作, 你需要在插件启用时加载所有已经在线的玩家的信息并将之储存在HashMap中.&lt;br /&gt;
&lt;br /&gt;
 for (Player player : Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
     playerList.put(player.getName(), playerData(player));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== 监听器  ==&lt;br /&gt;
监听器是一种方法被调用来对事件作出反应的类.所有的监听器使用 org.bukkit.event.Listener接口.更多有关监听器创建的细节,&lt;br /&gt;
:: &#039;&#039; 请查看: [[Event API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 指令  ==&lt;br /&gt;
&lt;br /&gt;
=== onCommand() 方法  ===&lt;br /&gt;
&lt;br /&gt;
 现在你已经知道如何注册一个事件并做出响应,但是如果你只是想要在命令输入之后做出响应呢? 你可以使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 这个代码的作用是当玩家输入“/”时，监听该操作并执行相关语句&amp;quot;/&amp;quot; . 举个例子来说.输入 &amp;quot;/do something&amp;quot; 将会执行 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 此种情况下，因为没有特定的行为被编程，所以并不会发生任何事情。&lt;br /&gt;
&lt;br /&gt;
 请避免使用和bukkit所提供的指令重名的指令, 然后深思你的指令名的唯一性。 &lt;br /&gt;
 &lt;br /&gt;
 例如.指令&amp;quot;give&amp;quot;已经被好几个插件使用了, 如果你执行了另外一个&amp;quot;give&amp;quot;指令, 你的插件将会和这些插件冲突。&lt;br /&gt;
&lt;br /&gt;
 你必须在插件的&amp;quot;plugins.yml&amp;quot;注册你的指令 否则这个方法将不会被触发。&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法必须返回一个布尔值（true或false）。 &lt;br /&gt;
 &lt;br /&gt;
 如果返回值是true，你不会看到什么明显的事情发生。 &lt;br /&gt;
 &lt;br /&gt;
 但如果返回值是false，则会返回你的plugin.yml里的&#039;usage:property&#039;然后发送给命令使用者. &lt;br /&gt;
&lt;br /&gt;
当使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时, 你需要填写4个参数. &lt;br /&gt;
&lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 发送命令的对象&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;Command cmd&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行的指令&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String commandLabel&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行指令的别名&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String[] args&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 该指令的自变量数组。&lt;br /&gt;
 &lt;br /&gt;
例如.指令 &#039;&#039;/hello abc def&#039;&#039; 中， &#039;&#039;abc&#039;&#039; 会被存放进args[0]中, &#039;&#039;def&#039;&#039; 被存放进args[1]中。&lt;br /&gt;
&lt;br /&gt;
==== 设置命令  ====&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { // 如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
     // 所需要执行的事（此处略）&lt;br /&gt;
         return true;&lt;br /&gt;
     } //如果以上内容成功执行，则返回true &lt;br /&gt;
     // 如果执行失败，则返回false.&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 每当使用&amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时， 在每个方法后面都加上一个 return false; 是一个惯例。 &lt;br /&gt;
 &lt;br /&gt;
 返回false会显示在plugin.yml里设置的usage信息 (看下面). 用这种方法的话，一旦执行出错便会显示usage信息。&lt;br /&gt;
&lt;br /&gt;
 每当方法return一个值的时候，这个方法就会退出，所以return true的时候，在它下面的代码就会被跳过, &lt;br /&gt;
 &lt;br /&gt;
 除非return语句在一个if的嵌套中，或者类似的嵌套情况。 &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;.equalsIgnoreCase(&amp;quot;basic&amp;quot;)&#039;&#039;&#039;&amp;lt;/code&amp;gt;代表忽略英文大小写. 在这种情况下,字符串&amp;quot;BAsIc&amp;quot; 和 &amp;quot;BasiC&amp;quot;和 basic相同，代码会被照常执行。&lt;br /&gt;
&lt;br /&gt;
和以前一样，加两行import在你的java文件头&lt;br /&gt;
&lt;br /&gt;
 import org.bukkit.command.Command;&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.command.CommandSender;&lt;br /&gt;
&lt;br /&gt;
=== 在Plugin.yml中添加你的指令  ===&lt;br /&gt;
&lt;br /&gt;
你需要添加你的指令到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件里. 如下是指令/basic添加到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039;的例子（请在plugins.yml的末尾添加如下代码）: &lt;br /&gt;
&lt;br /&gt;
commands:&lt;br /&gt;
   basic:&lt;br /&gt;
      description: This is a demo command.&lt;br /&gt;
      usage: /&amp;lt;command&amp;gt; [player]&lt;br /&gt;
      permission: &amp;lt;plugin name&amp;gt;.basic&lt;br /&gt;
      permission-message: You don&#039;t have &amp;lt;permission&amp;gt;&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;basic&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令名称&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;description&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令描述&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;usage&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 在oncommand方法return false后显示的用法提示。尽量简洁, 使别人能够理解指令是什么以及如何使用它。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 使用该命令所需的权限节点。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission-message&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 当玩家使用了这个指令而没有权限时输出的信息。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 PS:yml文件使用两个空格作为制表符,使用tab键输入制表符会导致错误。&lt;br /&gt;
&lt;br /&gt;
===控制台指令vs玩家指令===&lt;br /&gt;
&lt;br /&gt;
 你可能注意到了上文的&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt;参数. &amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt; 是个Bukkit接口，&lt;br /&gt;
 &lt;br /&gt;
 它有两个（对插件编写者）有用的子类：&amp;lt;code&amp;gt;&#039;&#039;&#039;Player&#039;&#039;&#039;&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;&#039;&#039;&#039;ConsoleCommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 所以当你编写插件的时候, 确定这个指令从控制台发出后能完全正常工作是十分必要的,&lt;br /&gt;
 &lt;br /&gt;
 那些只能由在线玩家执行的指令 &#039;&#039;只能&#039;&#039; 由一个在线玩家执行。&lt;br /&gt;
 &lt;br /&gt;
 有的插件在判断命令执 行对象不是玩家时仅仅用return进行处理 (比如当有人从控制台发出指令), &lt;br /&gt;
 &lt;br /&gt;
 即便这些指令能够在控制台完美运行（比如改变天气的指令）。&lt;br /&gt;
&lt;br /&gt;
一个判断sender的方法: &lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { //如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
             // 所需要执行的事（此处略）&lt;br /&gt;
             return true;&lt;br /&gt;
         } else if (cmd.getName().equalsIgnoreCase(&amp;quot;basic2&amp;quot;)) {&lt;br /&gt;
             if (!(sender instanceof Player)) { //如果sender与Player类不匹配&lt;br /&gt;
                 sender.sendMessage(&amp;quot;这个指令只能让玩家使用。&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 Player player = (Player) sender;&lt;br /&gt;
                    // 所需要执行的事（此处略）&lt;br /&gt;
             }&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 在这个例子里，指令&#039;&#039;&#039;basic&#039;&#039;&#039;能由任何人发出 - 一个在线的玩家, 或者服务器后台的op. &lt;br /&gt;
 &lt;br /&gt;
 但是指令&#039;&#039;&#039;basic2&#039;&#039;&#039;只能由一个在线的玩家发出. &lt;br /&gt;
&lt;br /&gt;
 大体上说,你应该允许尽可能多的指令在控制台和玩家聊天框里都正常执行. &lt;br /&gt;
 &lt;br /&gt;
 需要在线玩家执行的指令可以使用以上例子中的机制来检测&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;是一个玩家. &lt;br /&gt;
 &lt;br /&gt;
 很多指令广泛依靠于一些玩家的属性（例如可以被tp或被给予物品等等）。&lt;br /&gt;
&lt;br /&gt;
 如果你想更进一步, 你可以对你的指令的自变量做一些额外的检测， 例如传送指令在提供玩家ID的情况下（也只有在这种情况下）可以在控制台被执行。&lt;br /&gt;
&lt;br /&gt;
=== 使用独立的 CommandExecutor class  ===&lt;br /&gt;
&lt;br /&gt;
 以上的例子必须把 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法放在插件的主类里。&lt;br /&gt;
 &lt;br /&gt;
 对于小插件来说，这是极好的,但如果你要写一个大点的插件,把你的 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法放在独立的类里将会很有意义。&lt;br /&gt;
 &lt;br /&gt;
 幸运的是，这并不难: &lt;br /&gt;
&lt;br /&gt;
*在你的包里创建一个新类. 命名为&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;之类的 (当然，要把&#039;&#039;&#039;MyPlugin&#039;&#039;&#039;替换成你制作的插件的名字)。 这个类 &#039;&#039;&#039;必须&#039;&#039;&#039; 实现Bukkit 的&#039;&#039;&#039;CommandExecutor&#039;&#039;&#039;接口。&lt;br /&gt;
&lt;br /&gt;
*在你的插件的&amp;lt;code&amp;gt;&#039;&#039;&#039;onEnable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法中,你需要实例化你创建的命令执行类(CommandExecutor class), 然后做一个像下面一样的调用：&amp;lt;code&amp;gt;getCommand(&amp;quot;basic&amp;quot;).setExecutor(myExecutor);&amp;lt;/code&amp;gt;&lt;br /&gt;
 其中&amp;quot;basic&amp;quot;是我们需要操控的指令, &amp;lt;code&amp;gt;myExecutor&amp;lt;/code&amp;gt; 是我们创建的实例.&lt;br /&gt;
&lt;br /&gt;
实例: &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyPlugin.java&#039;&#039;&#039; (插件的主类): &lt;br /&gt;
 @Override&lt;br /&gt;
 public void onEnable() {&lt;br /&gt;
         // 如果你没有在plugins.yml注册过指令的话，此处会抛出空指针异常！&lt;br /&gt;
         this.getCommand(&amp;quot;basic&amp;quot;).setExecutor(new MyPluginCommandExecutor(this));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;MyPluginCommandExecutor.java&#039;&#039;&#039;（独立CommandExecutor类）: &lt;br /&gt;
 public class MyPluginCommandExecutor implements CommandExecutor {&lt;br /&gt;
         private final MyPlugin plugin;&lt;br /&gt;
 &lt;br /&gt;
         public MyPluginCommandExecutor(MyPlugin plugin) {&lt;br /&gt;
             this.plugin = plugin; // Store the plugin in situations where you need it.&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
             // 和以前一样执行就好…&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 请注意我们是如何把主类里的对象指向&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;的. 这允许我们轻易的使用主类对象的方法（如果我们需要的话）。&lt;br /&gt;
&lt;br /&gt;
 这么做的话, 我们便能更好的组织代码 - 如果 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法又大又复杂, 它就能被划分为子类，从而不使插件的主类显得凌乱。&lt;br /&gt;
&lt;br /&gt;
 PS：如果你的插件含有多个指令的话, 你需要为每一个指令单独设立一个commandexecutor.&lt;br /&gt;
&lt;br /&gt;
=== 写一个安全的指令 ===&lt;br /&gt;
 当写一个指令的时候,别进行任何假设（很重要）, 例如假设执行者一定是个玩家. 请牢记以下原则:&lt;br /&gt;
&lt;br /&gt;
==== 在命令执行前确定发出者是玩家 ====&lt;br /&gt;
&lt;br /&gt;
使用简单的例子来进行检测:&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (sender instanceof Player) {&lt;br /&gt;
             Player player = (Player) sender;&lt;br /&gt;
             // do something&lt;br /&gt;
         } else {&lt;br /&gt;
             sender.sendMessage(&amp;quot;You must be a player!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
             // do something&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 检测自变量个数 ====&lt;br /&gt;
&lt;br /&gt;
 别老是认为玩家都能打对正确数量的自变量. &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (args.length &amp;gt; 4) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Too many arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         } &lt;br /&gt;
         if (args.length &amp;lt; 2) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Not enough arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 在获取玩家之前先检测他们是否在线 ====&lt;br /&gt;
&lt;br /&gt;
 有时你想通过命令发出者打出的ID来获取其他玩家，请确保他们在线再说！&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         Player target = (Bukkit.getServer().getPlayer(args[0]));&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not online!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 如果你想对离线玩家进行操作，&amp;lt;code&amp;gt;&#039;&#039;&#039;OfflinePlayer&#039;&#039;&#039;&amp;lt;/code&amp;gt;类提供了基础操作的方法.&lt;br /&gt;
&lt;br /&gt;
== 插件的配置/设置  ==&lt;br /&gt;
Bukkit API 为插件提供了一种简便的方法，用来管理用户自定义的设置。同时，通过这种方法也可以储存数据。&lt;br /&gt;
:: &#039;&#039;详情请查看: [[Configuration API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 权限  ==&lt;br /&gt;
&lt;br /&gt;
利用新的Bukkit API来设置权限是再容易不过的事情. 检测玩家是否拥有权限，可以使用以下代码: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;if (player.hasPermission(&amp;quot;some.pointless.permission&amp;quot;)) {&lt;br /&gt;
   //执行此段代码(有权限)&lt;br /&gt;
} else {&lt;br /&gt;
   //执行此段代码(无权限)&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你也可以检测一个权限节点是否被定义过，(等效于 Java 语言中的&#039;&#039;&#039;null&#039;&#039;&#039;)，使用下面的方法: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;boolean isPermissionSet(String name)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你或许对此抱有疑惑:为什么这段代码中没有任何用户群的信息？因为它们并不是必需的. 之前这段代码想要实现的主要功能之一就是根据用户群格式化聊天信息. 但其实使用权限就可以很容易地完成这项工作.你可以在聊天插件的配置文件中定义权限与前缀的对应关系. 举例来说，&amp;quot;someChat.prefix.admin&amp;quot; 权限可以对应前缀[Admin].当拥有此权限的玩家发出聊天信息时 ，聊天前缀便是[Admin]. &lt;br /&gt;
&lt;br /&gt;
另一个想要实现的主要功能则是给所有拥有权限的在线用户发一条信息.你可以这样做: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;for (Player player: Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
    if (player.hasPermission(&amp;quot;send.receive.message&amp;quot;)) {&lt;br /&gt;
        player.sendMessage(&amp;quot;You were sent a message&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
看到这里你可能会问，若是没有权限组我该如何配置玩家的 [[权限]] ？既然 [[Bukkit]] API 本身并没有提供用户组的预设, 你必须通过安装一款权限组插件，像 [[permissionsBukkit]]  这样的插件来帮助你配置用户群. &#039;&#039;&#039;API 只是提供接口而不是提供已实现的类.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
=== 配置权限 ===&lt;br /&gt;
{{Hide|标题=译者注 |内容= 其一，以下代码中部分内容将以&#039;&#039;//文字&#039;&#039;翻译，使用时请不要加入其中内容，仅做教程中的翻译参考；&lt;br /&gt;
其二，default:true在下面翻译为全员默认拥有该权限，default:false则翻译为全员默认没有该权限，请注意理解。 }}&lt;br /&gt;
如果你想要更多地控制你的权限，例如设置权限的默认值或是子权限节点，不如将权限节点加入你的 &#039;&#039;[[plugin.yml]]&#039;&#039;文件.这并不是必须的，但我们推荐你这样做. 下面是一个实例，显示了当你将权限添加在&#039;&#039;plugin.yml&#039;&#039;文件末尾时的样子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands //给予使用doorman所有命令的权限&lt;br /&gt;
        children:  //子权限节点&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&lt;br /&gt;
    doorman.kick:&lt;br /&gt;
        description: Allows you to kick a user  //允许你踢出一名用户&lt;br /&gt;
        default: op  //OP默认拥有该权限&lt;br /&gt;
    doorman.ban:&lt;br /&gt;
        description: Allows you to ban a user  //允许你封禁一名用户&lt;br /&gt;
        default: op //OP默认拥有该权限&lt;br /&gt;
    doorman.knock:&lt;br /&gt;
        description: Knocks on the door! //敲门&lt;br /&gt;
        default: true //全员默认拥有该权限&lt;br /&gt;
    doorman.denied:&lt;br /&gt;
        description: Prevents this user from entering the door  //禁止拥有该权限节点的用户进入门内&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
值得注意的是,你的插件使用的每个权限都被定义为 &#039;&#039;permissions&#039;&#039; 这个节点的子权限节点. 每个权限都能够拥有一个描述，默认值以及子权限节点。&lt;br /&gt;
==== 默认值  ====&lt;br /&gt;
&lt;br /&gt;
当一个权限没有明确是否为玩家所拥有&amp;amp;nbsp;，&#039;&#039;hasPermission&#039;&#039;&amp;lt;ref&amp;gt;这是一个检测玩家是否拥有指定权限节点的方法&amp;lt;/ref&amp;gt;&amp;amp;nbsp;将返回false&amp;lt;ref&amp;gt;该方法返回的boolean值&amp;lt;/ref&amp;gt;. 在你的[[plugin.yml]] you can change this by setting the default node to be one of four values: &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;true&#039;&#039;&#039; - 全员默认拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;false&#039;&#039;&#039; - 全员默认没有该权限. &lt;br /&gt;
*&#039;&#039;&#039;op&#039;&#039;&#039; - 如果玩家是一名OP&amp;lt;ref&amp;gt;Operator，服务器管理员&amp;lt;/ref&amp;gt;，那么他将拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;not op&#039;&#039;&#039; - 如果玩家不是一名OP，那么他将拥有该权限.&lt;br /&gt;
&lt;br /&gt;
==== 子权限  ====&lt;br /&gt;
&lt;br /&gt;
在这之前你可能已经习惯了通过权限节点自动分配所有的子权限&amp;lt;ref&amp;gt;原句:Before now you will probably be used to the * permission to automatically assign all sub permissions.此处的sub permissions应是同上文“你的插件使用的每个权限都被定义为 permissions 这个节点的子权限节点.”相照应&amp;lt;/ref&amp;gt;. 现在这种方法已经随着bukkit API&amp;lt;ref&amp;gt;应用程序编程接口(Application Programming Interface)&amp;lt;/ref&amp;gt;的更新而改变，现在你可以自己定义子权限了. 这将使得插件具有更多的灵活性. 下面是一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands  //翻译见上&lt;br /&gt;
        children:&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
在这里，&#039;&#039;doorman.*&#039;&#039; 权限拥有几个子权限并分配在其下. 子权限工作的方式就是当&#039;&#039;doorman.*&#039;&#039; 权限节点被设置为true时,子权限也会随之设置为 &#039;&#039;plugin.yml&#039;&#039;中的默认值. 然而当&#039;&#039;doorman.*&#039;&#039; 被设置为false时，所有子权限都会设置为与默认值相反的值（true-&amp;gt;false,false-&amp;gt;true）.&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设置你自己的权限  ===&lt;br /&gt;
&lt;br /&gt;
如果你希望想知道更多有关开发你自己的权限插件的事情(即是设置权限的管理类插件) ，那么请前往查看教程&amp;amp;nbsp;[[Developing a permissions plugin]].&lt;br /&gt;
&lt;br /&gt;
== 计划任务与后台任务  ==&lt;br /&gt;
&lt;br /&gt;
目前, Minecraft服务器都使用一个线程进行所有逻辑操作, 所以每个发生在游戏中的逻辑运算必须要很短. 如果处理不好的话, 复杂的代码可能会导致你的服务器逻辑运算有极大的延迟和滞后. &lt;br /&gt;
&lt;br /&gt;
幸运的是, Bukkit正在帮助你安排你的插件源代码. 你可以提交一个在未来能够运行一次的可运行任务, 或者一个基础的循环, 或者你可以拆分出一个新的与游戏逻辑并行的线程, 用来执行过于冗长的任务.&lt;br /&gt;
&lt;br /&gt;
这里 [[Scheduler Programming]] 单独写出了计划程序的教程, 并给出了更多你可以用来安排同步任务的信息, 还有启动Bukkit的异步进程.&lt;br /&gt;
&lt;br /&gt;
== 方块操控&amp;lt;br&amp;gt;  ==&lt;br /&gt;
&lt;br /&gt;
这是一个很容易的方法创建、获取或修改一个方块, 例如当你想要修改往你头上数第五格的方块时, 首先你需要做的是获取你当前的坐标点, 然后往你当前的坐标点的Y轴加上五, 获取目标方块后我们便能够很容易地修改它了, 例子如下:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@EventHandler&lt;br /&gt;
public void onPlayerMove(PlayerMoveEvent event) {&lt;br /&gt;
    // 得到玩家当前的坐标.&lt;br /&gt;
    Location loc = event.getPlayer().getLocation();&lt;br /&gt;
    // 设置目标坐标为玩家头顶 5 格处.&lt;br /&gt;
    loc.setY(loc.getY() + 5);&lt;br /&gt;
    // 得到目标坐标的方块&lt;br /&gt;
    Block b = loc.getBlock();&lt;br /&gt;
    // 将目标坐标的方块更改为石头 (stone).&lt;br /&gt;
    b.setType(Material.STONE);&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
上面的代码获取了玩家的所在位置，获取头上的第五个方块，并且放上一个石头（stone）。 Note that once you have a &amp;lt;code&amp;gt;Block&amp;lt;/code&amp;gt;, there are other things you can do besides set its type. 你可以查阅JavaDoc获取更多的东西。&lt;br /&gt;
&lt;br /&gt;
你可以用这样相似的观念去生成一些建筑，并且还可以用编程的方式通过算法生成一些独特的方块（这里指由很多个方块拼在一起的四四方方的方块）。比如，去生成一个实心的方块建筑，你可能在生成建筑的时候还需要用到&amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;循环，去一遍遍循环来生成这些建筑，填满这些建筑等。&lt;br /&gt;
下面，是一个在指定坐标生成一个实心的方块建筑的实例方法：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void generateCube(Location loc, int length) {&lt;br /&gt;
    // 放置这个方块最中间的方块并且获取这个方块的坐标。&lt;br /&gt;
    // Uses getBlockN() instead of getN() to avoid casting to an int later.&lt;br /&gt;
    int x1 = loc.getBlockX(); &lt;br /&gt;
    int y1 = loc.getBlockY();&lt;br /&gt;
    int z1 = loc.getBlockZ();&lt;br /&gt;
&lt;br /&gt;
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.&lt;br /&gt;
    int x2 = x1 + length;&lt;br /&gt;
    int y2 = y1 + length;&lt;br /&gt;
    int z2 = z1 + length;&lt;br /&gt;
&lt;br /&gt;
    World world = loc.getWorld();&lt;br /&gt;
&lt;br /&gt;
    // Loop over the cube in the x dimension.&lt;br /&gt;
    for (int xPoint = x1; xPoint &amp;lt;= x2; xPoint++) { &lt;br /&gt;
        // Loop over the cube in the y dimension.&lt;br /&gt;
        for (int yPoint = y1; yPoint &amp;lt;= y2; yPoint++) {&lt;br /&gt;
            // Loop over the cube in the z dimension.&lt;br /&gt;
            for (int zPoint = z1; zPoint &amp;lt;= z2; zPoint++) {&lt;br /&gt;
                // Get the block that we are currently looping over.&lt;br /&gt;
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);&lt;br /&gt;
                // 设置方块ID为57号(钻石块!)&lt;br /&gt;
                currentBlock.setType(Material.DIAMOND_BLOCK); //这里下面会提到&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
这个方法将会在程序给予这个方法高度和起始坐标（最中间的方块的坐标）时，构建一个3D的方块建筑。简单的删除方块也可以使用这个方法的思路实现，但是，但是设置方块的时候，记得把上面标注会提到的那行，设置为物品ID为0的方块(空气方块)。&amp;lt;br&amp;gt; &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== (玩家)修改背包  ==&lt;br /&gt;
&lt;br /&gt;
这个部分大部分讲的是修改玩家的背包。但是在某些情况下，修改箱子里的内容与此相同，当然得在你能找到获取箱子里内容的方法才可以。&amp;amp;nbsp;:P. 这是一个修改玩家背包的一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void onPlayerJoin(PlayerJoinEvent evt) {&lt;br /&gt;
    Player player = evt.getPlayer(); // 当玩家加入游戏&lt;br /&gt;
    PlayerInventory inventory = player.getInventory(); // 获取玩家背包列表&lt;br /&gt;
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // 生成一组钻石&lt;br /&gt;
        &lt;br /&gt;
    if (inventory.contains(itemstack)) {&lt;br /&gt;
        inventory.addItem(itemstack); // 将一组钻石放到玩家的背包里&lt;br /&gt;
        player.sendMessage(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;); //向玩家发送消息(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
所以，上面代码里面的 onPlayerJoin 方法中，我们首先构建了一些变量&amp;lt;ref&amp;gt;或许应该是实例化的对象？不过原文中是variables，故此处直译 &amp;lt;/ref&amp;gt;，让我们的工作变得更容易一些。变量分别是： player, inventory and itemstack。 inventory 变量是玩家的背包内容，itemstack是那64个钻石的物品栈。然后我们判断了玩家是不是有一组钻石。如果有，我们就用inventory.addItem(itemstack)方法再给玩家一组钻石，并且给他发送一个消息“Wow！你看上去很土豪啊！”。 所以修改背包并不像我们想象的那么难，如果我们想实现让玩家进入游戏，检测到一组钻石后删掉这组钻石，只需要把代码里的inventory.addItem(itemstack)替换成inventory.remove(itemstack)。或许你还需要把“Wow！你看上去很土豪啊！”改成“Wow！你看上去好穷啊！”2333333。&lt;br /&gt;
&lt;br /&gt;
== 控制物品 ==&lt;br /&gt;
&lt;br /&gt;
当你需要写一个对物品们做出行为的代码时，你要使用ItemStack类来查阅有关那个栈的所有设置信息。&lt;br /&gt;
当然，你也可以干脆搞出一个特别的背包~  &lt;br /&gt;
&lt;br /&gt;
=== 物品容器GUI ===&lt;br /&gt;
你可以搞出一个类似于箱子的容器，只不过不是实体化的，它叫Inventory  。&lt;br /&gt;
&lt;br /&gt;
你可以到 https://hub.spigotmc.org/javadocs/bukkit/ &lt;br /&gt;
查找org.bukkit.inventory包了解详情&lt;br /&gt;
&lt;br /&gt;
构建方法：&lt;br /&gt;
 Inventory 变量 = Bukkit.createInventory(InventoryHolder ,int ,String);&lt;br /&gt;
 Inventory 变量 = Bukkit.createInventory(InventoryHolder,InventoryType ,String);&lt;br /&gt;
&lt;br /&gt;
所有者可以是null&lt;br /&gt;
&lt;br /&gt;
大小有两种，数字（int）或类型（InventoryType），需要注意的是，使用类型时，需要使用getDefaultSize()方法获取默认的大小。&lt;br /&gt;
&lt;br /&gt;
数字是9的倍数，不超过54，出来的是像箱子界面，如果超过54则...呵呵呵呵。&lt;br /&gt;
&lt;br /&gt;
类型是特殊的容器，如工作台界面，具体根据名称而定。&lt;br /&gt;
&lt;br /&gt;
类型列表(图片)：&lt;br /&gt;
&lt;br /&gt;
[[文件:InvType.jpg]]&lt;br /&gt;
&lt;br /&gt;
后面的String是你箱子的名字（标题）&lt;br /&gt;
&lt;br /&gt;
代码：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    Inventory inv1 = Bukkit.createInventory(null,InventoryType.CHEST , &amp;quot;seesaw_233&amp;quot;);&lt;br /&gt;
    Inventory inv2 = Bukkit.createInventory(null,54 , &amp;quot;§2 !seesaw!&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
inv1是普通小箱子界面，黑字体标题： seesaw_233&lt;br /&gt;
&lt;br /&gt;
inv2是大箱子界面,绿字体标题：  !seesaw!  &lt;br /&gt;
&lt;br /&gt;
颜色代码（§）列表（图）：&lt;br /&gt;
&lt;br /&gt;
[[文件:MinecraftColorCode.png]]&lt;br /&gt;
&lt;br /&gt;
让玩家关闭 容器/背包 的方法:&lt;br /&gt;
 &lt;br /&gt;
 closeInventory()&lt;br /&gt;
&lt;br /&gt;
让玩家打开指定容器的方法:&lt;br /&gt;
&lt;br /&gt;
 openInventory(容器变量)&lt;br /&gt;
&lt;br /&gt;
容器变量 是你指定的Inventory变量&lt;br /&gt;
&lt;br /&gt;
代码范例:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args ) &lt;br /&gt;
{&lt;br /&gt;
 if(cmd.getName().equalsIgnoreCase(&amp;quot;cg&amp;quot;) )&lt;br /&gt;
 {&lt;br /&gt;
     if(args.length ==0)&lt;br /&gt;
     {     &lt;br /&gt;
     sender.sendMessage(&amp;quot;§4 未知命令&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
    if(args.length ==1)&lt;br /&gt;
    {&lt;br /&gt;
        if (sender instanceof Player)&lt;br /&gt;
        {&lt;br /&gt;
            Player p = (Player)sender;            &lt;br /&gt;
            if(args[0].equalsIgnoreCase(&amp;quot;open&amp;quot;))&lt;br /&gt;
            {&lt;br /&gt;
                p.closeInventory();&lt;br /&gt;
                p.openInventory(inv1);&lt;br /&gt;
            &lt;br /&gt;
            }    &lt;br /&gt;
        } else{ sender.sendMessage(&amp;quot;§4 只有玩家才可使用该命令&amp;quot;); } &lt;br /&gt;
    }&lt;br /&gt;
 } &lt;br /&gt;
 return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 物品添加 ===&lt;br /&gt;
&lt;br /&gt;
有了容器，没有物品怎么行？&lt;br /&gt;
我们需要new出一个物品~&lt;br /&gt;
&lt;br /&gt;
 ItemStack 变量 = new ItemStack(类型 ,数量) ; &lt;br /&gt;
&lt;br /&gt;
类型同样可以是数字ID（int）或原版类型（Material） &lt;br /&gt;
&lt;br /&gt;
类型太多了，请到 https://hub.spigotmc.org/javadocs/bukkit/ &lt;br /&gt;
&lt;br /&gt;
查看org.bukkit.Material类的静态方法 2333333333333333333~&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
ItemStack myitem = new ItemStack(Material.DIAMOND_PICKAXE ,1) ; &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
lol~&lt;br /&gt;
&lt;br /&gt;
=== 附魔属性 ===&lt;br /&gt;
&lt;br /&gt;
你想给一个物品附魔，你首先需要了解[http://www.minecraftwiki.net/wiki/Data_values Item Code]和[http://www.minecraftwiki.net/wiki/Talk:Enchanting#Effect_IDs Effect ID].  Enchantments他们自己不能例示(new Enchantment() won&#039;t work) 因为他们是抽象的。所以你必须要用一个EnchantmentWrapper。如果你想去附魔物品，还不想在默认的SMP里附魔，可以使用addEnchantment()里面的addUnsafeEnchantment()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
int itemCode = 280;  //use the item code you want here&lt;br /&gt;
int effectId = 20;  //use the enchantment code you want here&lt;br /&gt;
int enchantmentLevel = 100;&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(itemCode);  //new item of item code&lt;br /&gt;
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id&lt;br /&gt;
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //给物品附魔&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 物品数据 ===&lt;br /&gt;
您可以通过这样做来设置物品的显示名称。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
String myDisplayName = &amp;quot;Awesome Sword&amp;quot;; //这里是保存了你想要显示的名字&lt;br /&gt;
 &lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你要更改的方块&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //得到方块的元数据&lt;br /&gt;
im.setDisplayName(myDisplayName); //设置它显示的名字&lt;br /&gt;
myItem.setItemMeta(im); //保存更改&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
你也可以设置一个物品的lore。 lore是物品的小注释, 像 &amp;quot;+5 攻击伤害&amp;quot; 在一个石剑上.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;String&amp;gt; lores = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
lores.add(&amp;quot;123&amp;quot;);  //第一行lore&lt;br /&gt;
lores.add(&amp;quot;abc&amp;quot;)  //这条lore在123之后&lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你的物品&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again&lt;br /&gt;
im.setLore(lores); //add the lores of course&lt;br /&gt;
myItem.setItemMeta(im); //give the item the new itemmeta&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
更多关于lore的设置和它所依靠的ArrayList参见  [[ArrayList Reference]]&lt;br /&gt;
&lt;br /&gt;
=== 容器控制 ===&lt;br /&gt;
&lt;br /&gt;
容器不控制咋行呢，不能让人乱来。&lt;br /&gt;
&lt;br /&gt;
这次要扯到监听器，先放前置代码：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void onInventoryClick(InventoryClickEvent event) &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
Inventory inv = Bukkit.createInventory(null,InventoryType.CHEST , &amp;quot;hello&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
Player p = (Player)event.getWhoClicked();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
我们先要判断操作者是不是玩家（真的有非玩家操作物品的事）&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
if(event.getWhoClicked() instanceof Player == false) { return;}    // 如果操作对象不是玩家则返回 &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后看TA是不是在操作我们指定的容器（用容器标题判定）&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
if (event.getInventory().getTitle().equalsIgnoreCase(&amp;quot;hello&amp;quot;) ) &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
我们可以使用 getRawSlot() 来判断TA打开的是什么位置。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
if(event.getRawSlot() == 0 )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
需要知道的是，数组是从0开始计数的，左上角第一个格子的位置为0 &lt;br /&gt;
&lt;br /&gt;
具体如图所示&lt;br /&gt;
&lt;br /&gt;
[[文件:InvNumber.jpg]]&lt;br /&gt;
&lt;br /&gt;
如果不想让TA拿走物品，使用:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
event.setCancelled(true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
但是这会有延迟，并且如果在拿走的一瞬间关闭界面可能会刷走物品&lt;br /&gt;
&lt;br /&gt;
所以可以先关闭界面再打开容器&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
p.closeInventory();&lt;br /&gt;
p.openInventory(inv);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
代码范例:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void onInventoryClick(InventoryClickEvent event) &lt;br /&gt;
{&lt;br /&gt;
  {&lt;br /&gt;
  if (event.getWhoClicked() instanceof Player == false) { return;} &lt;br /&gt;
  Player p = (Player)event.getWhoClicked();&lt;br /&gt;
  if (event.getInventory().getTitle().equalsIgnoreCase(&amp;quot; §2 !DC! &amp;quot;) ) &lt;br /&gt;
     {&lt;br /&gt;
     event.setCancelled(true);&lt;br /&gt;
     p.updateInventory();       &lt;br /&gt;
         if(event.getRawSlot() == 0 )&lt;br /&gt;
         { &lt;br /&gt;
             p.closeInventory();&lt;br /&gt;
             p.openInventory(inv);    &lt;br /&gt;
         }  &lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
版权所有，违者不究~&lt;br /&gt;
&lt;br /&gt;
== Maps, and Sets, and Lists 天呐！ &amp;lt;ref&amp;gt;标题中Map,Set,List均为Java类集中的接口，故不翻译&amp;lt;/ref&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
除了Map类和HashMap类，Java中还提供了其它的数据结构类集。 &lt;br /&gt;
&lt;br /&gt;
他们提供这些不同的类，是因为有些时候，使用Map类并不合适。&amp;lt;nowiki&amp;gt;[[Java data structure classes]]&amp;lt;/nowiki&amp;gt;在这个条目中，有对Java数据框架类集更深入的讲述。&lt;br /&gt;
&lt;br /&gt;
=== HashMaps概览以及如何使用此接口  === &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{模板:待修正}}&lt;br /&gt;
记住永远不要在一个HashMap直接使用Player玩家！&lt;br /&gt;
&lt;br /&gt;
应在HashMap中使用String字符串，使用 &amp;quot;p.getName()&amp;quot; 来添加，移除或是检查列表中是否含有某个玩家.&lt;br /&gt;
&lt;br /&gt;
将玩家当作object储存在HashMap中会造成大量内存泄漏(就是费机器资源).&lt;br /&gt;
&lt;br /&gt;
当做一个插件时，你会得到一个点，只使用单变量事件发生或条件已经满足不了由于多个玩家执行的动作/事件。 &lt;br /&gt;
&lt;br /&gt;
这是我的问题，我有一个旧的插件，Zones，现在被改进，重新命名为Regions 。我得到了这些错误的大部分，因为我没有考虑插件会如何在一个实际的服务器上的行为，在任何给定的时间超过一个。我过去使用一个独立的布尔值变量来检测玩家是否在选区中，显然当每个玩家的值需要被区分开来时这个方法将不能正常工作。所以如果一个玩家在选区内而另一个在选区外，那么这个变量将会一直保持改变，因此也会造成许多错误。&lt;br /&gt;
&lt;br /&gt;
HashMap是这样做的一个好方法。HashMap是一种映射/分配的一个关键值。你可以设置HashMap，关键是玩家的值可以做你想做的事情，但与HashMap的有用的东西是一个key的只能包含一个值，不能有重复的key。比如说，我把“adam”作为key，并赋予它“a”的值。那工作的打算，但后来说，后来我想指定值“B”为重点的“adam”我能会没有错误，但价值”“分配到关键的“adam”在HashMap将覆盖因为与哈希图不包含重复的值。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Defining a HashMap  ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public Map&amp;lt;KeyType, DataType&amp;gt; HashMapName = new HashMap&amp;lt;&amp;gt;(); //Example syntax&lt;br /&gt;
// Example Declaration&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; pluginEnabled = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; isGodMode = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
记住代码因为我们会使用它的教程的其余部分在与哈希图。因此，例如，我们可以创建一个简单的函数，它将切换插件是否已启用或不启用。首先，在你的命令功能中，我解释了你需要创建一个函数，将玩家的名字发送到函数，并相应地调整玩家的状态。&lt;br /&gt;
&lt;br /&gt;
所以在命令的内部，你需要这个，函数名可以是不同的，但为了简单，它是最好的，如果你保持不变。&lt;br /&gt;
&lt;br /&gt;
 public boolean onCommand(Commandsender sender, Command cmd, String label, String args[])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
Player player = (Player) sender;&lt;br /&gt;
togglePluginState(player);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
这上面的代码将发送到玩家的值传递给函数的参数togglepluginstate()&lt;br /&gt;
&lt;br /&gt;
但现在我们需要建立我们的togglepluginstate()功能。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void togglePluginState(Player player) &lt;br /&gt;
{&lt;br /&gt;
  // 请注意我们如何使用这个玩家名为这里的 key&lt;br /&gt;
  // 不是玩家对象&lt;br /&gt;
  String playerName = player.getName(); &lt;br /&gt;
&lt;br /&gt;
    if (pluginEnabled.containsKey(playerName)) &lt;br /&gt;
    {&lt;br /&gt;
&lt;br /&gt;
        if (pluginEnabled.get(playerName)) &lt;br /&gt;
        {&lt;br /&gt;
            pluginEnabled.put(playerName, false);&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin disabled&amp;quot;);&lt;br /&gt;
        } &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            pluginEnabled.put(playerName, true);&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    } &lt;br /&gt;
    else &lt;br /&gt;
    {&lt;br /&gt;
        pluginEnabled.put(playerName, true); &lt;br /&gt;
        //如果你想要的插件默认情况下为禁用更改此值为 false&lt;br /&gt;
        player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在，这个代码是做个检查，如果HashMap首先包含的关键玩家，所以如果它被放入HashMap，如果那时候我们检查HashMap关键值获得（玩家）；如果这是真的然后设定值假送玩家一个消息，如果这个值是false，然后做相反，设定值为true，发送一个消息了。但如果HashMap不包含关键的玩家，那么我们可以认为这是他们的第一次 运行/使用 所以我们改变默认值，增加玩家的HashMap&lt;br /&gt;
&lt;br /&gt;
==== More Ideas for HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
一个HashMap（或是任何一种Map在Java）是一个关联系统。它可以快速、高效地查找某种值，赋予独特的key。在任何情况下，这会发生在您的代码，地图可能是您的解决方案。&lt;br /&gt;
&lt;br /&gt;
这里有一些其他的想法非常适合使用map。正如你所看到的，它不必是你存储的数据或你的玩家，但可以是任何类型的数据，需要“翻译”从一个形式到另一个。&lt;br /&gt;
&lt;br /&gt;
===== Data Value Lookups  =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public Map&amp;lt;String, Integer&amp;gt; wool_colors = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):&lt;br /&gt;
wool_colors.put(&amp;quot;orange&amp;quot;, 1);&lt;br /&gt;
wool_colors.put(&amp;quot;magenta&amp;quot;, 2);&lt;br /&gt;
wool_colors.put(&amp;quot;light blue&amp;quot;, 3);&lt;br /&gt;
wool_colors.put(&amp;quot;black&amp;quot;, 15);&lt;br /&gt;
// Run this in response to user commands - turn &amp;quot;green&amp;quot; into 13&lt;br /&gt;
&lt;br /&gt;
int datavalue = 0;&lt;br /&gt;
public boolean onCommand(Commandsender sender, Command cmd, String label, String args[])&lt;br /&gt;
    if (wool_colors.containsKey(argument)) &lt;br /&gt;
    { datavalue = wool_colors.get(argument); } &lt;br /&gt;
    else &lt;br /&gt;
    {&lt;br /&gt;
        try &lt;br /&gt;
        { datavalue = Integer.parseInt(argument); }&lt;br /&gt;
        catch (Exception e) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Saving/Loading a HashMap  ====&lt;br /&gt;
&lt;br /&gt;
一旦你知道如何使用与哈希图，你可能想知道如何保存和加载的HashMap数据。&lt;br /&gt;
&lt;br /&gt;
*您不希望管理员手动编辑数据&lt;br /&gt;
&lt;br /&gt;
* 你需要以二进制格式保存数据&lt;br /&gt;
&lt;br /&gt;
*你想避免解析块名称 和/或 其他物体从任意文本上&lt;br /&gt;
&lt;br /&gt;
这是很简单的方法如何保存任何HashMap。你可以替换HashMap ,amp ,String, Integer；任何你想要的HashMap类型。让我们举个例子让HashMap代码保存它：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HashMap&amp;lt;String, Integer&amp;gt; mapToSave = new HashMap&amp;lt;String,Integer&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
public void save(HashMap&amp;lt;String, Integer&amp;gt; map, String path) &lt;br /&gt;
{&lt;br /&gt;
    try &lt;br /&gt;
    {&lt;br /&gt;
         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
         oos.writeObject(map);&lt;br /&gt;
         oos.flush();&lt;br /&gt;
        oos.close();&lt;br /&gt;
    } &lt;br /&gt;
    catch(Exception e) // Hand a IOexceptions&lt;br /&gt;
    { e.printStackTrace(); }&lt;br /&gt;
}&lt;br /&gt;
// ...&lt;br /&gt;
save(mapToSave, getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你可以看到它真的很容易。加载工作非常相似但我们使用对象输入流而不是对象，而是个FileInputStream，代替writeobject() 让我们返回HashMap。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public HashMap&amp;lt;String, Integer&amp;gt; load(String path) &lt;br /&gt;
{&lt;br /&gt;
    try &lt;br /&gt;
    {&lt;br /&gt;
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
        Object result = ois.readObject();&lt;br /&gt;
        // 如果你知道这文件中的HashMap ，你可以随意投结果HashMap &amp;lt;字符串，整数&amp;gt;&lt;br /&gt;
        return (HashMap&amp;lt;String, Integer&amp;gt;)result;&lt;br /&gt;
    } &lt;br /&gt;
    catch(Exception e) &lt;br /&gt;
    { e.printStackTrace(); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
HashMap&amp;lt;Integer, String&amp;gt; loadedMap;&lt;br /&gt;
&lt;br /&gt;
String path = getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
File file = new File(path);&lt;br /&gt;
&lt;br /&gt;
if (file.exists()) // 检查是否有文档&lt;br /&gt;
{ loadedMap  = load(path); }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你可以使用这个“API”保存/加载与哈希图，数组列表，并执行序列化或外部接口的所有对象。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
/** SLAPI = Saving/Loading API&lt;br /&gt;
&lt;br /&gt;
 * API for Saving and Loading Objects.&lt;br /&gt;
&lt;br /&gt;
 * Everyone has permission to include this code in their plugins as they wish :)&lt;br /&gt;
&lt;br /&gt;
 * @author Tomsik68&amp;lt;tomsik68@gmail.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 */&lt;br /&gt;
public class SLAPI&lt;br /&gt;
{&lt;br /&gt;
    public static &amp;lt;T extends Object&amp;gt; void save(T obj,String path) throws Exception&lt;br /&gt;
    {&lt;br /&gt;
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
    oos.writeObject(obj);&lt;br /&gt;
    oos.flush();&lt;br /&gt;
    oos.close();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static &amp;lt;T extends Object&amp;gt; T load(String path) throws Exception&lt;br /&gt;
    {&lt;br /&gt;
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
    T result = (T)ois.readObject();&lt;br /&gt;
    ois.close();&lt;br /&gt;
    return result;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Example implementation of this API: &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;I&#039;m skipping some part of code in this source&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class Example extends JavaPlugin &lt;br /&gt;
{&lt;br /&gt;
private ArrayList&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() &lt;br /&gt;
    {&lt;br /&gt;
        try &lt;br /&gt;
        { list = SLAPI.load(&amp;quot;example.bin&amp;quot;); } &lt;br /&gt;
        catch(Exception e) // handle the exception&lt;br /&gt;
        { e.printStackTrace(); }&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    @Override&lt;br /&gt;
    public void onDisable()	&lt;br /&gt;
    {&lt;br /&gt;
        try &lt;br /&gt;
        { SLAPI.save(list,&amp;quot;example.bin&amp;quot;); } &lt;br /&gt;
        catch(Exception e) &lt;br /&gt;
        { e.printStackTrace(); }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note #1: 这将未修改的几乎所有知名Java类型如整型，字符串，HashMap。它还将为一些Bukkit类型以及工作。如果你在写你自己的数据对象类，你可能想利用这一技术保存它们的状态，你应该阅读关于Java的序列化或外部接口。可外化和序列化的唯一区别是，自动将所有的，序列化的类的域和试图序列化，而外部的允许你定义的阅读和写作的对象的方法。它很容易添加到您的代码，它会使您的数据持久性非常小的工作需要。没有更多的解析！&lt;br /&gt;
&lt;br /&gt;
Note #2: 该API不支持变化。一旦你改变了类中的东西，你的插件的旧版本保存的数据文件将无法正确加载。 &lt;br /&gt;
&lt;br /&gt;
===== Tips &amp;amp; Examples =====&lt;br /&gt;
&lt;br /&gt;
1. 简化你的储蓄结构&lt;br /&gt;
&lt;br /&gt;
让要被保存的数据越简单越好, 例如: 如果你要保存一个Player对象,你可以保存该对象的UUID/Name来代替; 如果你想要保存Localtion对象, 你可以保存它的x,y,z以及World对象UUID. &#039;&#039;&#039;不要直接保存Bukkit的原始类型&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
2. 保存版本号以及数据&lt;br /&gt;
&lt;br /&gt;
You should always remember, that you don&#039;t know what you&#039;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&#039;t know!&lt;br /&gt;
&lt;br /&gt;
3. 迁移旧文件&lt;br /&gt;
&lt;br /&gt;
If your plugin finds older version of some file, it should update the file accordingly and change version number.&lt;br /&gt;
&lt;br /&gt;
== 元数据  ==&lt;br /&gt;
&lt;br /&gt;
Bukkit正在尝试尽可能的让制作插件变得更简单，所以带有一个键值例如Player, Entity, World，甚至是一个block的HashMaps都可以用元数据取缔掉。元数据是一些替代HashMap的方式。他可以允许你去自定义&amp;quot;fields&amp;quot;给Players, Entities, Worlds and Blocks.这些东西都是元数据类的一个成员(详见[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&#039;re creating an economy plugin, you would need a HashMap of Player and Float or Double. 如果用了元数据，你不必去做! You just attach to player new metadata 值, and that&#039;s it!&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== 为何要去使用元数据  ===&lt;br /&gt;
&lt;br /&gt;
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps. &lt;br /&gt;
* 元数据可以用来跟其他插件分享信息与数据。&lt;br /&gt;
&lt;br /&gt;
=== 为什么不去使用元数据&amp;lt;br&amp;gt;  ===&lt;br /&gt;
&lt;br /&gt;
* Slightly more difficult to get the value.&lt;br /&gt;
* It is not saved on shutdown (but then again, neither are any Maps that you create).&lt;br /&gt;
&lt;br /&gt;
=== 获取 &amp;amp;amp; 设置元数据  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {&lt;br /&gt;
  object.setMetadata(key, new FixedMetadataValue(plugin,value));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Object getMetadata(Metadatable object, String key, Plugin plugin) {&lt;br /&gt;
  List&amp;lt;MetadataValue&amp;gt; values = object.getMetadata(key);  &lt;br /&gt;
  for (MetadataValue value : values) {&lt;br /&gt;
     // Plugins are singleton objects, so using == is safe here&lt;br /&gt;
     if (value.getOwningPlugin() == plugin) {&lt;br /&gt;
        return value.value();&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
  return null;&lt;br /&gt;
}&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note: If you&#039;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.&lt;br /&gt;
&lt;br /&gt;
== Databases  ==&lt;br /&gt;
&lt;br /&gt;
Sometimes flat files aren&#039;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).&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there&#039;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!) &lt;br /&gt;
&lt;br /&gt;
=== SQLite  ===&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;s very straightforward and shouldn&#039;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]. &lt;br /&gt;
&lt;br /&gt;
SQLite is great for very simple databases, because there&#039;s no server concerns to set up. Just make a few calls to create a new database and table. It&#039;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&#039;s often easiest and fastest to get the SQL basics squared away with SQLite, even if you &amp;quot;graduate&amp;quot; to a more server-class database engine later.&lt;br /&gt;
&lt;br /&gt;
=== MySQL  ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 简易定时器 ==&lt;br /&gt;
&lt;br /&gt;
要定时当然是用 BukkitScheduler&lt;br /&gt;
&lt;br /&gt;
要定时就和long脱不了干系&lt;br /&gt;
&lt;br /&gt;
long是一种整数数据类型，数值后面要加 &amp;lt;big&amp;gt;L&amp;lt;/big&amp;gt; 如:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 public long a = 233L ;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Java的时间单位是1000L一秒&lt;br /&gt;
&lt;br /&gt;
Bukkit的时间单位是20L一秒&lt;br /&gt;
&lt;br /&gt;
还要扯到Bukkit的线程系统上&lt;br /&gt;
&lt;br /&gt;
Bukkit服务器是使用一条线程运行的，所以在插件里随便搞java线程是非常危险的&lt;br /&gt;
&lt;br /&gt;
说白了，当你在插件里用这条代码时:&lt;br /&gt;
&lt;br /&gt;
 Thread.sleep(5*1000L);&lt;br /&gt;
  // 你认为是插件“睡”五秒&lt;br /&gt;
&lt;br /&gt;
整个服务器会停止五秒...&lt;br /&gt;
&lt;br /&gt;
所以要用Bukkit提供的东西&lt;br /&gt;
&lt;br /&gt;
这里将会使用BukkitRunnable定时&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
new BukkitRunnable()&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    int time = 60;  // 六十秒&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void run() &lt;br /&gt;
    {&lt;br /&gt;
        if(time == 0)&lt;br /&gt;
        {&lt;br /&gt;
           cancel();  // 终止线程&lt;br /&gt;
             return;&lt;br /&gt;
        }&lt;br /&gt;
        // your code ...&lt;br /&gt;
    }&lt;br /&gt;
                &lt;br /&gt;
}.runTaskTimer(this, 0L, 20L);&lt;br /&gt;
          // 插件主类  延时  定时&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 随机器 ==&lt;br /&gt;
&lt;br /&gt;
这玩意经常出现在娱乐性插件中，主要用它来抽某个玩家&lt;br /&gt;
&lt;br /&gt;
Random实现的随机算法是伪随机，也就是有规则的随机。在进行随机时，随机算法的起源数字称为种子数(seed)，在种子数的基础上进行一定的变换，从而产生需要的随机数字。&lt;br /&gt;
&lt;br /&gt;
但是相同种子数的Random对象，相同次数生成的随机数字是完全相同的。这点在生成多个随机数字时需要特别注意。&lt;br /&gt;
&lt;br /&gt;
接下来是使用普通jar运行，所以会有大量 &amp;lt;big&amp;gt;static&amp;lt;/big&amp;gt; 关键字，请注意&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     public static void say(Object the) //输出&lt;br /&gt;
    {&lt;br /&gt;
        System.out.print(the);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
用Random有两种方法&lt;br /&gt;
&lt;br /&gt;
 public static Random rd = new Random();&lt;br /&gt;
 rd.*&lt;br /&gt;
&lt;br /&gt;
 (new Random()).*&lt;br /&gt;
&lt;br /&gt;
接下来用第二种来讲&lt;br /&gt;
&lt;br /&gt;
Random在很多地方都能学到，这里只举插件常用的int作为例子&lt;br /&gt;
&lt;br /&gt;
 (new Random()).nextInt(整数);&lt;br /&gt;
&lt;br /&gt;
整数就是上限，随机范围从 0 ~ 整数-1&lt;br /&gt;
&lt;br /&gt;
每一个随机数的几率都是相等的，也就是说 0123456789他们的几率都是 10%&lt;br /&gt;
&lt;br /&gt;
但如果要随机范围最低不是0呢&lt;br /&gt;
&lt;br /&gt;
放个丧心病狂的代码:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    public static void ran(int start ,int to)&lt;br /&gt;
    {&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;); &lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);&lt;br /&gt;
         say( (new Random()).nextInt(start)+to +&amp;quot; &amp;quot;);   &lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public static void main(String[] args) &lt;br /&gt;
{ ran(5,2); }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
取值范围是 2 ~ 2+(5-1)&lt;br /&gt;
&lt;br /&gt;
弄一方程式(设 start=x ，to=y )&lt;br /&gt;
 x ~ x+(y-1)&lt;br /&gt;
&lt;br /&gt;
结果就是(一行一次结果):&lt;br /&gt;
 6 5 6 6 3 3 6 6 5 6&lt;br /&gt;
 4 3 2 4 5 2 2 3 6 2&lt;br /&gt;
 6 3 3 5 4 2 5 2 2 2&lt;br /&gt;
&lt;br /&gt;
那怎么随机玩家呢，这就要扯到ArrayList了&lt;br /&gt;
&lt;br /&gt;
ArrayList可以直接储存Player 所以...&lt;br /&gt;
&lt;br /&gt;
看看HotPotato（烫手山芋）是怎么挑中某个倒霉的玩家的吧&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void giveRandomPotato()&lt;br /&gt;
{&lt;br /&gt;
	if (getAlivePlayers().size() &amp;lt;= 1)  // 如果只剩一个玩家&lt;br /&gt;
		return;&lt;br /&gt;
	GamePlayer gp = (GamePlayer)getAlivePlayers().get((new Random()).nextInt(getAlivePlayers().size() - 1));  // 就是这里 &lt;br /&gt;
	gp.getPlayer().getInventory().setHelmet(new ItemStack(Material.TNT));  // 呵呵&lt;br /&gt;
	gp.getPlayer().getInventory().addItem(new ItemStack[] {&lt;br /&gt;
		ItemUtil.potato()  &lt;br /&gt;
	});&lt;br /&gt;
	potatoPlayer = gp.getPlayer();&lt;br /&gt;
	GamePlayer gamePlayer;&lt;br /&gt;
	for (Iterator iterator = gamePlayers.iterator(); iterator.hasNext(); MessageUtil.sendTextMessage(gamePlayer.getPlayer(), &amp;quot;newPotato&amp;quot;, gp.getPlayer().getName()))&lt;br /&gt;
	gamePlayer = (GamePlayer)iterator.next();&lt;br /&gt;
&lt;br /&gt;
	new PotatoTimer(this);  // 另一个定时器(Boom!)&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public List getAlivePlayers()&lt;br /&gt;
{&lt;br /&gt;
	List alive = new ArrayList();&lt;br /&gt;
	for (Iterator iterator = gamePlayers.iterator(); iterator.hasNext();)&lt;br /&gt;
	{     // 遍历列表中的玩家&lt;br /&gt;
		GamePlayer gamePlayer = (GamePlayer)iterator.next();&lt;br /&gt;
		if (gamePlayer.isAlive())&lt;br /&gt;
			alive.add(gamePlayer);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return alive;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
但如果要抽取多个玩家呢?&lt;br /&gt;
&lt;br /&gt;
可用方法太多，时间成本太贵，举个不是例子的例子。&lt;br /&gt;
&lt;br /&gt;
 其实是懒 ~&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public class Support &lt;br /&gt;
{&lt;br /&gt;
    public List allplayer; &lt;br /&gt;
    public List allplayerCopy; // 拷贝&lt;br /&gt;
    public List hider;&lt;br /&gt;
    public List seeker;&lt;br /&gt;
    public Random rd; // 随机器 &lt;br /&gt;
    public Support()&lt;br /&gt;
    {&lt;br /&gt;
        allplayer = new ArrayList()&lt;br /&gt;
        {{&lt;br /&gt;
            add(&amp;quot;player01&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player02&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player03&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player04&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player05&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player06&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player07&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player08&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player09&amp;quot;);&lt;br /&gt;
            add(&amp;quot;player10&amp;quot;);&lt;br /&gt;
        }};&lt;br /&gt;
        allplayerCopy = new ArrayList();&lt;br /&gt;
         allplayerCopy.clear();&lt;br /&gt;
         allplayerCopy.addAll(allplayer);&lt;br /&gt;
        hider = new ArrayList();&lt;br /&gt;
        seeker = new ArrayList();&lt;br /&gt;
        rd = new Random();&lt;br /&gt;
        ran(3);&lt;br /&gt;
        System.out.print(&amp;quot;hider: &amp;quot;+ hider + &amp;quot;   &amp;quot;);&lt;br /&gt;
        System.out.print(&amp;quot;seeker: &amp;quot;+ seeker);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    public void ran(int amount)&lt;br /&gt;
    {&lt;br /&gt;
        int ta; // 数量&lt;br /&gt;
        Object player ;  // 模拟玩家&lt;br /&gt;
        if(allplayerCopy.size() &amp;lt;= 1) &lt;br /&gt;
        { return; }&lt;br /&gt;
        for (ta = amount;ta &amp;gt; 0; ta--)&lt;br /&gt;
        {&lt;br /&gt;
            player = allplayerCopy.get(rd.nextInt(allplayerCopy.size() )); // 抽取一个玩家&lt;br /&gt;
            seeker.add(player);&lt;br /&gt;
            allplayerCopy.remove(player); &lt;br /&gt;
        }&lt;br /&gt;
        hider.clear();&lt;br /&gt;
        hider.addAll(allplayerCopy);&lt;br /&gt;
        allplayerCopy.clear();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
结果:&lt;br /&gt;
&lt;br /&gt;
 hider: [player01, player02, player03, player05, player06, player08, player10] &lt;br /&gt;
 seeker: [player09, player07, player04]&lt;br /&gt;
&lt;br /&gt;
 hider: [player02, player05, player06, player07, player08, player09, player10]   &lt;br /&gt;
 seeker: [player04, player03, player01]&lt;br /&gt;
&lt;br /&gt;
 hider: [player01, player03, player04, player06, player07, player09, player10]   &lt;br /&gt;
 seeker: [player08, player02, player05]&lt;br /&gt;
 &lt;br /&gt;
不要问我10号模拟玩家(String)怎么回事，他人品好~&lt;br /&gt;
&lt;br /&gt;
如果要搞几率呢？&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 if((new Random().nextInt(100)+1) &amp;lt; 50){&lt;br /&gt;
//50%的几率&lt;br /&gt;
}else{&lt;br /&gt;
//另外50%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 部署你的插件  ==&lt;br /&gt;
&lt;br /&gt;
=== Eclipse 构建方法 ===&lt;br /&gt;
&lt;br /&gt;
 总之你写好的你的插件,如何把它变成一个可安装在服务端的jar文件呢?&lt;br /&gt;
 &lt;br /&gt;
 首先我们需要建立一个水桶服务器。 [http://wiki.bukkit.org/Setting_up_a_server 在这里] 能找到开服相关信息.接下来你需要把插件导出为一个.jar文件，&lt;br /&gt;
 &lt;br /&gt;
 这样你就能在你的服务器里运行它。 在Eclipse里, 右键工程，点击&#039;&#039;Run as &amp;gt; Maven install&#039;&#039;即可:&lt;br /&gt;
&lt;br /&gt;
[[Image:Maveninstall.png]]&lt;br /&gt;
&lt;br /&gt;
 将来呢, 当你对插件进行了改动后,在做上述操作之前，如果你想删除之前的.jar文件，你可以右键工程，点击 &#039;&#039;Run as &amp;gt; Maven clean&#039;&#039; 。 &lt;br /&gt;
 &lt;br /&gt;
 如果你在编译插件时遇到了问题,  请检查你的JDK是否正确安装， 并浏览[[建立工作区]]条目。&lt;br /&gt;
 &lt;br /&gt;
 如果你在Eclipse控制台看到了和JDK有关的错误，你可能需要手动调整JDK,因为Eclipse的检测可能发生了错误。 &lt;br /&gt;
 &lt;br /&gt;
 点击&#039;&#039;Window -&amp;gt; Preferences&#039;&#039;, 然后是&#039;&#039;Java -&amp;gt; Installed JREs&#039;&#039;。选中你最近安装的JDK作为Java运行环境（JRE）并添加, 并取消掉原来那个报错的JDK:&lt;br /&gt;
&lt;br /&gt;
[[Image:Jrelocation.png]]&lt;br /&gt;
&lt;br /&gt;
 如果你的工程成功被编译,JAR文件会生产在 &#039;&#039;target&#039;&#039; 目录下，具体在Eclipse的工作空间里名字为工程名的文件夹中。&lt;br /&gt;
 &lt;br /&gt;
 这个JAR文件就是一个能正常工作的Bukkit插件了。当然前提是你的plugin.yml里没出差错。&lt;br /&gt;
 &lt;br /&gt;
 你可以把这个jar文件丢到服务器的plugins文件夹里,重载或重启服务器,就可以测试你的新插件啦。&lt;br /&gt;
 &lt;br /&gt;
 为了连接上使用你自己的电脑开的服务器 ,在多人游戏的ip里填上你的本地ip即可。&lt;br /&gt;
 &lt;br /&gt;
 如果出现了错误而你又不能解决的话, 试试这里[http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], &lt;br /&gt;
 &lt;br /&gt;
 在这里提问[http://wiki.bukkit.org/IRC bukkitdev IRC channel],或者重新阅读wiki。&lt;br /&gt;
 &lt;br /&gt;
 当你做完了一个实用的插件,可以考虑把它发表在这里[http://dev.bukkit.org/ dev.bukkit]（为了Bukkit社区的访问量）。&lt;br /&gt;
 &lt;br /&gt;
 以上可知,JAR格式是默认压缩生成的(JAR文件基于ZIP文件的格式)。这么说来，在上传插件到Bukkit上时把JAR文件压缩成ZIP只会增加文件体积罢了。&lt;br /&gt;
 &lt;br /&gt;
 而且呢, 放在JAR中的config文件可以在检测不到插件目录（插件自动生成的文件夹，名字与插件名相同，里面是该插件的配置）下的config文件时自动生成。&lt;br /&gt;
 &lt;br /&gt;
 所以我们一般不把jar文件转换成其他格式。 &lt;br /&gt;
&lt;br /&gt;
=== NetBeanIDE 构建方法 ===&lt;br /&gt;
&lt;br /&gt;
[[文件:NetBeansIDE-build.jpg]]&lt;br /&gt;
&lt;br /&gt;
然后到项目文件所在地打开dist文件夹就能看到了&lt;br /&gt;
&lt;br /&gt;
== 导入其它插件 ==&lt;br /&gt;
你可能会希望编辑其它那些有源代码的插件. 如果插件在它的目录下有&#039;&#039;pom.xml&#039;&#039;(大多数知名插件,例如WorldEdit和Essentials都有),你可以通过点击&#039;&#039;File -&amp;gt; Import&#039;&#039;来把它作为一个工程导入, 然后打开&#039;&#039;Maven&#039;&#039;项并选择&#039;&#039;Existing Maven Projects&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
[[Image:Importmaven.png]]&lt;br /&gt;
&lt;br /&gt;
然后选择&#039;&#039;pom.xml&#039;&#039;所在的目录,这个工程就应该出现在你的侧边栏里了。就像平常一样编辑并编译它吧。&lt;br /&gt;
&lt;br /&gt;
== 小贴士  ==&lt;br /&gt;
&lt;br /&gt;
=== 使玩家着火 ===&lt;br /&gt;
&lt;br /&gt;
 Bukkit API有很多碉堡的功能。以下是实现一些功能的代码片段。 &lt;br /&gt;
&lt;br /&gt;
下面的代码运行一个玩家使另一个玩家着火。通过类似于&#039;&#039;&#039;/ignite Notch&#039;&#039;&#039;这样的指令，这个指令会让 Notch 着火。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     // 使用 equalsIgnoreCase() 来忽视大小写&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;ignite&amp;quot;)) {&lt;br /&gt;
         // 确保玩家只输入了一个自变量(要被点着的玩家名).&lt;br /&gt;
         if (args.length != 1) {&lt;br /&gt;
             // 当return false时，该指令的使用信息会显示出来。&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 确保命令发出者是个玩家.&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can set other players on fire.&amp;quot;);&lt;br /&gt;
             sender.sendMessage(&amp;quot;This is an arbitrary requirement for demonstration purposes only.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 获取那个要被点着的玩家. 请注意下标起始是0，不是1.&lt;br /&gt;
         Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
 &lt;br /&gt;
         // 确保这个玩家在线。&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 让这个玩家着火1000 ticks (一秒钟大概有20ticks,所以总共是50秒).&lt;br /&gt;
         target.setFireTicks(1000);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 干掉玩家  ===&lt;br /&gt;
&lt;br /&gt;
 和标题一样.这里是要干掉某个玩家。 &lt;br /&gt;
&lt;br /&gt;
在你的onCommand方法里使用这个: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;KillPlayer&amp;quot;)) {&lt;br /&gt;
         Player target = sender.getServer().getPlayer(args[0]);&lt;br /&gt;
          // 确保玩家在线.&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         target.setHealth(0); &lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
这里有个小扩展,会使用一个爆炸来杀死玩家: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 float explosionPower = 4F; //爆炸等级 - TNT的爆炸等级默认是4F&lt;br /&gt;
 Player target = sender.getWorld().getPlayer(args[0]);&lt;br /&gt;
 target.getWorld().createExplosion(target.getLocation(), explosionPower);&lt;br /&gt;
 target.setHealth(0);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 创造一个假爆炸  ===&lt;br /&gt;
&lt;br /&gt;
这段代码会生产一个看起来和听起来与TNT/苦力怕产生的爆炸一样的效果。但是不会对实体和方块产生破坏。 在保留爆炸的艺术的同时又削弱了爆炸的威力，真是太有用了&lt;br /&gt;
&amp;lt;source lang=&#039;java&#039;&amp;gt;&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onExplosionPrime(ExplosionPrimeEvent event) {	&lt;br /&gt;
     Entity entity = event.getEntity();&lt;br /&gt;
 &lt;br /&gt;
     // 如果事件和点燃的TNT有关,执行一些代码（又略）&lt;br /&gt;
     if (entity instanceof TNTPrimed) {&lt;br /&gt;
         entity.getWorld().createExplosion(entity.getLocation(), 0);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 使一个玩家对另外一个玩家隐形  ===&lt;br /&gt;
&lt;br /&gt;
 通过输入指令，能让指令执行者对特定的玩家隐形。 然而其他人仍然能看到指令执行者。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;HideMe&amp;quot;) &amp;amp;&amp;amp; args.length == 1) {&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can use this command!&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         // 检测指令执行者是玩家后，我们便可以安全的进行强制转换类操作。&lt;br /&gt;
        Player s = (Player) sender;&lt;br /&gt;
 &lt;br /&gt;
        // 获取命令执行者指定的玩家&lt;br /&gt;
        Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
        if (target == null) {&lt;br /&gt;
            sender.sendMessage(&amp;quot;Player &amp;quot; + args[0] + &amp;quot; is not online.&amp;quot;);&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        // 将 s 从 target 的视野中隐藏起来.&lt;br /&gt;
        target.hidePlayer(s);&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在玩家准星所指处生产闪电 ===&lt;br /&gt;
&lt;br /&gt;
如下代码运行手持鱼竿的玩家通过点击来生成闪电 (当然要用准星瞄准)。 真是个简单好玩的恶作剧（。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onPlayerInteractBlock(PlayerInteractEvent event) {&lt;br /&gt;
     Player player = event.getPlayer();&lt;br /&gt;
     if (player.getItemInHand().getType() == Material.FISHING_ROD) {&lt;br /&gt;
         // 在给定坐标中生成一道闪电. 在本例中, 这个坐标是玩家准星瞄准的地方.&lt;br /&gt;
         // 只能指向200格以内的坐标.&lt;br /&gt;
         player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 玩家摔不死 ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 @EventHandler&lt;br /&gt;
public void onEntityDamage(EntityDamageEvent event)&lt;br /&gt;
    {&lt;br /&gt;
        Entity target = event.getEntity();&lt;br /&gt;
	org.bukkit.event.entity.EntityDamageEvent.DamageCause cause = event.getCause();		&lt;br /&gt;
            if (target instanceof Player)&lt;br /&gt;
                {&lt;br /&gt;
                if (cause == org.bukkit.event.entity.EntityDamageEvent.DamageCause.FALL )&lt;br /&gt;
			{&lt;br /&gt;
				event.setCancelled(true);&lt;br /&gt;
			}&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 自动格式 ===&lt;br /&gt;
&lt;br /&gt;
 Eclipse提供了将你的代码自动转成Oracle规定的格式, 修正了不符合常规的缩进, 空格等等。 在侧边栏选中你的项目,然后选择&#039;&#039;Source -&amp;gt; Format&#039;&#039;即可对其进行更改。&lt;br /&gt;
&lt;br /&gt;
=== 代码集锦 ===&lt;br /&gt;
&lt;br /&gt;
看现成的总好过绞尽脑汁~&lt;br /&gt;
&lt;br /&gt;
如果你想在动手做一个实用插件时不想死一堆脑细胞，这里是你的好去处&lt;br /&gt;
&lt;br /&gt;
警告: 其中黑科技出没!&lt;br /&gt;
&lt;br /&gt;
点我点我 ---&amp;gt;  [[插件代码集中营]]&lt;br /&gt;
&lt;br /&gt;
 为伸手党而生~&lt;br /&gt;
&lt;br /&gt;
== 插件需求专区  ==&lt;br /&gt;
&lt;br /&gt;
http://forums.bukkit.org/forums/plugin-requests.13/&lt;br /&gt;
&lt;br /&gt;
== 范例文件和样板  ==&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/wpeTPx7N Example.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have any more questions on this matter, don&#039;t hesitate to visit the BukkitDev [[IRC]] channel and ask!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=3426</id>
		<title>Event API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=3426"/>
		<updated>2016-08-15T14:16:47Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 基础 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Event API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
服务器里发生的事情，服务器会用&#039;&#039;&#039;事件&#039;&#039;&#039; 来告诉你。 &#039;&#039;&#039;Bukkit&#039;&#039;&#039;中有各种各样的事件，例如 玩家的互动(玩家登录, 玩家点击了一个方块, 玩家死了,玩家重生...), 方块的动态(方块被放置, 方块被破坏, 方块附近的实体被改变...), 实体事件(一个怪物看上了你233, 一个苦力怕帮你收集土方块...), 世界事件 (一个世界被加载或卸载, 一个区块被加载或卸载)等等. 由于MC官方的一些原因，他们的JavaDocs都挂了= =&lt;br /&gt;
&lt;br /&gt;
== 基础 ==&lt;br /&gt;
为了保持简单易懂，这一节只讨论一种事件——PlayerLoginEnvent。让我们从创建一个监听器开始吧。&lt;br /&gt;
&lt;br /&gt;
=== 创建方法 ===&lt;br /&gt;
为了让你的插件能够监听并处理该事件的回调，你需要为它创建一个方法，这个方法就是事件&#039;&#039;&#039;监听器&#039;&#039;&#039;。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
你需要向Bukkit注册你的监听器，这样服务器才会在&amp;quot;PlayerLoginEvent&amp;quot;触发时调用该监听器。我们需要给监听器打上 @EventHandler 批注。&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
&amp;quot;@EventHandler&amp;quot; 是一个能打在某个方法上的批注，就像这样：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // 优先级 默认为NORMAL级&amp;lt;/source&amp;gt;&lt;br /&gt;
这样一来，这所对应的方法就成为了一个 优先级 为NORMAL的监听器。&lt;br /&gt;
&lt;br /&gt;
你可以设置EventHandler标签的EventPriority属性来自定义事件的优先级，例如：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 将优先级设为HIGHEST级&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 优先级设为LOW级&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你的监听器应该看上去像是这样：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加监听器 ===&lt;br /&gt;
为了让Bukkit能够注册你的监听器，你需要给你的监听器所在的类实现Listener接口(org.bukkit.event.Listener)。例如这样：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
监听器的方法名（例子中是“onlogin”的东西）并不重要；只要符合命名规则，起什么名字，你开心就好。&lt;br /&gt;
你可能想知道：“Bukkit怎么知道监听那个事件呢？”。Bukkit是通过上面例子中你定义的“PlayerLoginEvent”这个参数知道的。&lt;br /&gt;
{{warning|一个监听器只能监听一个事件，否则Bukkit是不会注册它的！}}&lt;br /&gt;
你的插件的主类 (就是继承JavaPlugin的那个类) 也可以成为一个事件监听器。在你的插件非常小的时候，这样就很方便了。比如:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    getLogger().log(Level.INFO, &amp;quot;Player &amp;quot; + event.getPlayer().getName() + &amp;quot; is logging in!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EventHandler的参数 ===&lt;br /&gt;
@EventHandler 批注可以设置某些参数来改变监听器的某些作用等。在某些情况下，你可以设置这些参数:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! 名称&lt;br /&gt;
! 类型&lt;br /&gt;
! 默认值&lt;br /&gt;
! 描述&lt;br /&gt;
! 值&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| 设置监听器优先级&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| 如果设为true，在事件被其他监听器关闭的时候，你的监听器将监听不到这个事件&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
Bukkit中的监听器拥有六个优先级&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
&lt;br /&gt;
每个插件可能会在一个事件触发时做出不同的相应，并且有的插件可能想更快的知道一个事件的发生。并且，有时候Bukkit传递事件给每个插件的时候，甚至这个事件已经被其他插件关闭了。某些情况下，其他插件刚把这个事件关掉，另外一个插件监听到了以后又把这个事件开开了。这样乱七八糟的冲突发生了以后，这就很尴尬了。&lt;br /&gt;
&lt;br /&gt;
比如 方块放置 被触发了，LOWEST优先级的监听器将会最先对这个事件做出决定，比如关闭它什么的。然后LOW优先级的监听器将会被触发，这时候可以重写LOWEST级监听器刚刚做出的修改，以此类推。Eventually it hits monitor, and at this point nothing should change the outcome of the event. MONITOR级的监听器 should be used to see the outcome of an event, without changing any aspect of it.&lt;br /&gt;
If we have three plugins enabled; one is a basic area protection plugin, one is a fancy plugin using signs, and another is a logging plugin.&lt;br /&gt;
The protection plugin listens on Priority.LOWEST. It says they can&#039;t place blocks in this area, and cancels the event.&lt;br /&gt;
The fancy sign plugin listens on Priority.NORMAL. It says they can place signs here, and uncancels the event.&lt;br /&gt;
The log plugin listens on Priority.MONITOR. It sees that the event was actually allowed, and logs it.&lt;br /&gt;
&lt;br /&gt;
If you want to change the outcome of an event, choose very carefully from LOWEST to HIGHEST. Suggested generalized protection plugins on lowest, more specific plugins on normal, and override plugins on high.&lt;br /&gt;
If you want to act when an event happens, but not change the outcome, use MONITOR. &#039;&#039;&#039;It&#039;s really really important that you use MONITOR, or an event might get cancelled after you&#039;ve acted on it, and it&#039;s even more important that you don&#039;t change the outcome of the event on MONITOR or it&#039;ll break other plugins.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;中文简单解释&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
优先级越高的方法越早调用&lt;br /&gt;
&lt;br /&gt;
优先级再高(除了MONITOR) setCanceled() 方法也不能阻止其它插件监听到它&lt;br /&gt;
&lt;br /&gt;
在MONITOR上搞 setCanceled(false) 其它插件会给你玩死~&lt;br /&gt;
&lt;br /&gt;
== 注册事件(监听器) ==&lt;br /&gt;
要注册你的方法，包含EventHandler的类必须是implent了Listenr类的&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
你只需要提供一个插件和一个监听器来在PluginManager里注册它们&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听器示例 ===&lt;br /&gt;
这个监听器包含两个EventHandlers，一个监听级别是HIGH并且另一个是NORMAL。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在插件中注册事件监听器 ===&lt;br /&gt;
The registerEvents method requires a listener and a plugin. Luckily, we already have our LoginListener. Now for the LoginPlugin!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 在实现监听器接口的插件中注册事件监听器 ====&lt;br /&gt;
&amp;lt;ref&amp;gt;准确来说是在实现了监听器接口的继承了JavaPlugin的类中注册事件（笑&amp;lt;/ref&amp;gt;&lt;br /&gt;
You could even have the events in the main class, for example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在监听器中注册事件监听器 ===&lt;br /&gt;
There are many ways to register your events. Here&#039;s an example where you register them in your listener class.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The LoginPlugin would look like this:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 在监听器中注销事件监听器 ==&lt;br /&gt;
You can un-register individual events, entire listener classes or all events registered by your plugin or even by other plugins!&lt;br /&gt;
&lt;br /&gt;
==== 注销特定的事件监听器 ====&lt;br /&gt;
Each event class has the getHandlerList() static method, call that and then you can use .unregister() method.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// this will unregister all PlayerInteractEvent instances from the plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Now you know why you&#039;ll need the getHandlerList() in your custom events.&lt;br /&gt;
&lt;br /&gt;
==== 注销所有事件监听器 ====&lt;br /&gt;
Using the HandlerList class and its unregisterAll() static method you can easily unregister events from listener classes or plugins.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// this will unregister all events from the specified plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
Creating custom events is very simple, you can use the same system that Bukkit uses without ruining performance.&lt;br /&gt;
&lt;br /&gt;
There are two (2) things to keep in mind when you create a Custom Event. They are &amp;quot;extend Event&amp;quot; and &amp;quot;static handlers.&amp;quot; With static handlers, you must input the following code into your custom event:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This block of code makes the EventHandlers contained inside your own event, keeping any unrelated events completely separated.&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件示例 ===&lt;br /&gt;
The following example shows how easy it is to create your own &amp;quot;CustomEvent.&amp;quot;&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 调用你的自定义事件 ====&lt;br /&gt;
You are in control of creating and calling your events, where you call it is completely up to you. Here&#039;s an example&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// Create the event here&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// Call the event&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// Now you do the event&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember: You are in control of your events. If you don&#039;t call it, and act upon it, it doesn&#039;t happen!&lt;br /&gt;
&lt;br /&gt;
==== 监听一个自定义事件 ====&lt;br /&gt;
How do you listen to a custom event you say? Simple, the same way as listening to a normal event!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 使得你的自定义事件具有可撤销性 ====&lt;br /&gt;
If you ever want to make your event cancellable, remember one thing: &amp;quot;implements Cancellable.&amp;quot; Just like you would import Listener. It&#039;s really that simple, let me show you an example!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Afterwards, you would check if a plugin had cancelled the event in your code, before processing normally&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// Create the event here&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// Call the event&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// Check if the event is not cancelled&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // Now you do the event&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 视频教程 ==&lt;br /&gt;
If you would prefer to watch a video tutorial version of this, please click [http://www.youtube.com/watch?v=PWQNsqwD-AY here].&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
[[Category: Tutorials]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=3425</id>
		<title>Event API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Event_API_Reference&amp;diff=3425"/>
		<updated>2016-08-15T13:55:56Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 介绍 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Event API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
服务器里发生的事情，服务器会用&#039;&#039;&#039;事件&#039;&#039;&#039; 来告诉你。 &#039;&#039;&#039;Bukkit&#039;&#039;&#039;中有各种各样的事件，例如 玩家的互动(玩家登录, 玩家点击了一个方块, 玩家死了,玩家重生...), 方块的动态(方块被放置, 方块被破坏, 方块附近的实体被改变...), 实体事件(一个怪物看上了你233, 一个苦力怕帮你收集土方块...), 世界事件 (一个世界被加载或卸载, 一个区块被加载或卸载)等等. 由于MC官方的一些原因，他们的JavaDocs都挂了= =&lt;br /&gt;
&lt;br /&gt;
== 基础 ==&lt;br /&gt;
To keep this section simple, we&#039;re going to only work with PlayerLoginEvent. Lets start with setting up the method&lt;br /&gt;
为了保持简单易懂，这一节只讨论一种事件——PlayerLoginEnvent。让我们从创建一个监听/处理该事件的方法开始吧。&lt;br /&gt;
&lt;br /&gt;
=== 创建方法 ===&lt;br /&gt;
In order for your plugin to handle an event call, you need to create a method for it:&lt;br /&gt;
为了让你的插件能够监听并处理该事件的回调，你需要为它创建一个方法。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Before this method can be invoked by Bukkit when the &amp;quot;PlayerLoginEvent&amp;quot; is fired, we need to annotate it. We do this with EventHandlers.&lt;br /&gt;
你还需要向Bukkit声明一下，这样服务器才会在&amp;quot;PlayerLoginEvent&amp;quot;触发时调用该函数。我们需要用到EventHandlers来做到这一点。&lt;br /&gt;
&lt;br /&gt;
=== @EventHandler ===&lt;br /&gt;
The &amp;quot;@EventHandler&amp;quot; class is an Annotation, which goes just above your method. It looks like this:&lt;br /&gt;
“@EventHandler”类是一个Annotation，一般就放在你的方法上面。就像这样：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler // EventPriority（事件优先级）默认为NORMAL&amp;lt;/source&amp;gt;&lt;br /&gt;
This marks your method as an EventHandler with the EventPriority as NORMAL.&lt;br /&gt;
这样一来，这一方法就标记为了一个EventPriority（事件优先级）为NORMAL的EventHandler（事件监听/处理器）。&lt;br /&gt;
&lt;br /&gt;
The EventHandler can take an EventPriority to specify the priority of the method, like so:&lt;br /&gt;
EventHandler可以通过EventPriority来自定义事件的优先级，例如：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler(priority = EventPriority.HIGHEST) // 方法优先级设为最高&lt;br /&gt;
@EventHandler(priority = EventPriority.LOW) // 方法优先级设为低&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s what it would look like in your class:&lt;br /&gt;
你的回调函数看起来应该和下面这个相类似：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@EventHandler&lt;br /&gt;
public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    // 你自己的代码...&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 添加监听器 ===&lt;br /&gt;
为了bukkit能够注册你的时间处理器，包含你事件处理方法的类必须implement了Listener接口(org.bukkit.event.Listener),例如：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public final class MyPlayerListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Your code here...&lt;br /&gt;
    }&lt;br /&gt;
} &amp;lt;/source&amp;gt;&lt;br /&gt;
这个方法的名字（在上面的例子中的“onlogin”）并不重要；你可以给你的监听器任何只要你开心就好的名字。&lt;br /&gt;
你可能想知道：“bukkit怎么知道监听那个事件呢？”bukkit通过上面例子中你定义的“PlayerLoginEvent”这个方法的事件参数知道的。&lt;br /&gt;
{{warning|你必须定义一个单个的事件否则Bukkit不会注册它的}}&lt;br /&gt;
你的插件的主类 (即extends JavaPlugin的那个类)也可以成为一个事件监听器, 并且这个可能在你的插件非常小的时候起作用，比如:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public class MyPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
  @Override&lt;br /&gt;
  public void onEnable() {&lt;br /&gt;
    getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @EventHandler&lt;br /&gt;
  public void onLogin(PlayerLoginEvent event) {&lt;br /&gt;
    getLogger().log(Level.INFO, &amp;quot;Player &amp;quot; + event.getPlayer().getName() + &amp;quot; is logging in!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EventHandler参数 ===&lt;br /&gt;
The @EventHandler annotation can take parameters to further define how the event handler behaves. At the moment you can specify:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;5&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Description&lt;br /&gt;
! Values&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| priority&lt;br /&gt;
| EventPriority&lt;br /&gt;
| EventPriority.NORMAL&lt;br /&gt;
| Sets the priority of your method&lt;br /&gt;
|&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| ignoreCancelled&lt;br /&gt;
| boolean&lt;br /&gt;
| false&lt;br /&gt;
| If set to true, your method will not get the event if the event has been cancelled&lt;br /&gt;
| &lt;br /&gt;
* true&lt;br /&gt;
* false&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 事件优先级 ====&lt;br /&gt;
There are six priorities in Bukkit that are called in the following order&lt;br /&gt;
* EventPriority.LOWEST&lt;br /&gt;
* EventPriority.LOW&lt;br /&gt;
* EventPriority.NORMAL&lt;br /&gt;
* EventPriority.HIGH&lt;br /&gt;
* EventPriority.HIGHEST&lt;br /&gt;
* EventPriority.MONITOR&lt;br /&gt;
&lt;br /&gt;
Every plugin gets a say in what happens, and every plugin must get a chance to know the outcome of an event. So, we pass events to plugins even after they&#039;ve been cancelled. A plugin can actually uncancel an event after another plugin cancelled it. This is where priorities become really important.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say a BLOCK_PLACE event is being handled. The lowest priority listener is called to get its say in whether it should be cancelled or not. Then the low priority listener is called to see if it wants to override the lowest, etc. Eventually it hits monitor, and at this point nothing should change the outcome of the event. Monitor should be used to see the outcome of an event, without changing any aspect of it.&lt;br /&gt;
If we have three plugins enabled; one is a basic area protection plugin, one is a fancy plugin using signs, and another is a logging plugin.&lt;br /&gt;
The protection plugin listens on Priority.LOWEST. It says they can&#039;t place blocks in this area, and cancels the event.&lt;br /&gt;
The fancy sign plugin listens on Priority.NORMAL. It says they can place signs here, and uncancels the event.&lt;br /&gt;
The log plugin listens on Priority.MONITOR. It sees that the event was actually allowed, and logs it.&lt;br /&gt;
&lt;br /&gt;
If you want to change the outcome of an event, choose very carefully from LOWEST to HIGHEST. Suggested generalized protection plugins on lowest, more specific plugins on normal, and override plugins on high.&lt;br /&gt;
If you want to act when an event happens, but not change the outcome, use MONITOR. &#039;&#039;&#039;It&#039;s really really important that you use MONITOR, or an event might get cancelled after you&#039;ve acted on it, and it&#039;s even more important that you don&#039;t change the outcome of the event on MONITOR or it&#039;ll break other plugins.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;中文简单解释&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
优先级越高的方法越早调用&lt;br /&gt;
&lt;br /&gt;
优先级再高(除了MONITOR) setCanceled() 方法也不能阻止其它插件监听到它&lt;br /&gt;
&lt;br /&gt;
在MONITOR上搞 setCanceled(false) 其它插件会给你玩死~&lt;br /&gt;
&lt;br /&gt;
== 注册事件(监听器) ==&lt;br /&gt;
要注册你的方法，包含EventHandler的类必须是implent了Listenr类的&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
你只需要提供一个插件和一个监听器来在PluginManager里注册它们&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;getServer().getPluginManager().registerEvents(Listener, Plugin);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 监听器示例 ===&lt;br /&gt;
这个监听器包含两个EventHandlers，一个监听级别是HIGH并且另一个是NORMAL。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在插件中注册事件监听器 ===&lt;br /&gt;
The registerEvents method requires a listener and a plugin. Luckily, we already have our LoginListener. Now for the LoginPlugin!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new LoginListener(), this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 在实现监听器接口的插件中注册事件监听器 ====&lt;br /&gt;
&amp;lt;ref&amp;gt;准确来说是在实现了监听器接口的继承了JavaPlugin的类中注册事件（笑&amp;lt;/ref&amp;gt;&lt;br /&gt;
You could even have the events in the main class, for example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin implements Listener {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        getServer().getPluginManager().registerEvents(this, this);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 在监听器中注册事件监听器 ===&lt;br /&gt;
There are many ways to register your events. Here&#039;s an example where you register them in your listener class.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
import org.bukkit.event.EventPriority;&lt;br /&gt;
import org.bukkit.event.player.PlayerLoginEvent;&lt;br /&gt;
&lt;br /&gt;
public final class LoginListener implements Listener {&lt;br /&gt;
    public LoginListener(LoginPlugin plugin) {&lt;br /&gt;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @EventHandler(priority = EventPriority.HIGH)&lt;br /&gt;
    public void highLogin(PlayerLoginEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The LoginPlugin would look like this:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
&lt;br /&gt;
public final class LoginPlugin extends JavaPlugin {&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        new LoginListener(this);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 在监听器中注销事件监听器 ==&lt;br /&gt;
You can un-register individual events, entire listener classes or all events registered by your plugin or even by other plugins!&lt;br /&gt;
&lt;br /&gt;
==== 注销特定的事件监听器 ====&lt;br /&gt;
Each event class has the getHandlerList() static method, call that and then you can use .unregister() method.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
PlayerInteractEvent.getHandlerList().unregister(plugin);&lt;br /&gt;
// this will unregister all PlayerInteractEvent instances from the plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Now you know why you&#039;ll need the getHandlerList() in your custom events.&lt;br /&gt;
&lt;br /&gt;
==== 注销所有事件监听器 ====&lt;br /&gt;
Using the HandlerList class and its unregisterAll() static method you can easily unregister events from listener classes or plugins.&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
HandlerList.unregisterAll(plugin);&lt;br /&gt;
// this will unregister all events from the specified plugin&lt;br /&gt;
// you can also specify a listener class instead of plugin.&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 创建自定义事件 ==&lt;br /&gt;
Creating custom events is very simple, you can use the same system that Bukkit uses without ruining performance.&lt;br /&gt;
&lt;br /&gt;
There are two (2) things to keep in mind when you create a Custom Event. They are &amp;quot;extend Event&amp;quot; and &amp;quot;static handlers.&amp;quot; With static handlers, you must input the following code into your custom event:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
&lt;br /&gt;
public HandlerList getHandlers() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static HandlerList getHandlerList() {&lt;br /&gt;
    return handlers;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
This block of code makes the EventHandlers contained inside your own event, keeping any unrelated events completely separated.&lt;br /&gt;
&lt;br /&gt;
=== 自定义事件示例 ===&lt;br /&gt;
The following example shows how easy it is to create your own &amp;quot;CustomEvent.&amp;quot;&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 调用你的自定义事件 ====&lt;br /&gt;
You are in control of creating and calling your events, where you call it is completely up to you. Here&#039;s an example&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// Create the event here&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// Call the event&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// Now you do the event&lt;br /&gt;
Bukkit.getServer().broadcastMessage(event.getMessage());&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember: You are in control of your events. If you don&#039;t call it, and act upon it, it doesn&#039;t happen!&lt;br /&gt;
&lt;br /&gt;
==== 监听一个自定义事件 ====&lt;br /&gt;
How do you listen to a custom event you say? Simple, the same way as listening to a normal event!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Listener;&lt;br /&gt;
import org.bukkit.event.EventHandler;&lt;br /&gt;
&lt;br /&gt;
public final class CustomListener implements Listener {&lt;br /&gt;
    @EventHandler&lt;br /&gt;
    public void normalLogin(CustomEvent event) {&lt;br /&gt;
        // Some code here&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 使得你的自定义事件具有可撤销性 ====&lt;br /&gt;
If you ever want to make your event cancellable, remember one thing: &amp;quot;implements Cancellable.&amp;quot; Just like you would import Listener. It&#039;s really that simple, let me show you an example!&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import org.bukkit.event.Event;&lt;br /&gt;
import org.bukkit.event.HandlerList;&lt;br /&gt;
import org.bukkit.event.Cancellable;&lt;br /&gt;
&lt;br /&gt;
public final class CustomEvent extends Event implements Cancellable {&lt;br /&gt;
    private static final HandlerList handlers = new HandlerList();&lt;br /&gt;
    private String message;&lt;br /&gt;
    private boolean cancelled;&lt;br /&gt;
&lt;br /&gt;
    public CustomEvent(String example) {&lt;br /&gt;
        message = example;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public String getMessage() {&lt;br /&gt;
        return message;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public boolean isCancelled() {&lt;br /&gt;
        return cancelled;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public void setCancelled(boolean cancel) {&lt;br /&gt;
        cancelled = cancel;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public HandlerList getHandlers() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public static HandlerList getHandlerList() {&lt;br /&gt;
        return handlers;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Afterwards, you would check if a plugin had cancelled the event in your code, before processing normally&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// Create the event here&lt;br /&gt;
CustomEvent event = new CustomEvent(&amp;quot;Sample Message&amp;quot;);&lt;br /&gt;
// Call the event&lt;br /&gt;
Bukkit.getServer().getPluginManager().callEvent(event);&lt;br /&gt;
// Check if the event is not cancelled&lt;br /&gt;
if (!event.isCancelled()) {&lt;br /&gt;
    // Now you do the event&lt;br /&gt;
    Bukkit.getServer().broadcastMessage(event.getMessage());&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 视频教程 ==&lt;br /&gt;
If you would prefer to watch a video tutorial version of this, please click [http://www.youtube.com/watch?v=PWQNsqwD-AY here].&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
[[Category: Tutorials]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3424</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3424"/>
		<updated>2016-08-15T13:51:13Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 自定义配置文件 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太过时了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin]的时候, 这个类也会继承JavaPlugin这个父类的的方法和属性。 在被继承的方法其中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次使用&#039;&#039;getConfig()&#039;&#039;的时候，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; ，同时从jar中的&#039;&#039;config.yml&#039;&#039;加载相应的默认值。 需要注意的是，第一次使用之后再调用&#039;&#039;getConfig()&#039;&#039;只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的。除非你手动保存了，否则你对这个对象进行的修改并不会被保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;之后，对磁盘上的config.yml文件的修改不会被反映在这个对象里。如果在plugins文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你对一个 &#039;&#039;FileConfiguration&#039;&#039; 对象进行了修改操作，或者改变了某些 &#039;&#039;Lists&#039;&#039;，如果你想保存你的修改，让插件被卸载了这些数据也能得以保存的话，你可以把你修改的数据保存到硬盘里。保存文件到硬盘里，你可以借助 saveConfig 方法。这会重写硬盘里的那份配置文件。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件来存储其他的配置信息，或储存额外的游戏信息配置文件等，你可以自己写一个你自己的方法，来仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 类里的 getConfig，reloadConfig，saveConfig 方法实现这一目的。下面是一个例子，可以告诉你如何编写一个方法，来读取和保存自定义配置文件。由于这些配置文件属于你的插件，你可以把这个方法放在你的主类，这样用起来跟原来一样。你必须为每个YAML文件写的一套方法。不过你可以使用以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3423</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3423"/>
		<updated>2016-08-15T13:46:48Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 保存文件 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太过时了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin]的时候, 这个类也会继承JavaPlugin这个父类的的方法和属性。 在被继承的方法其中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次使用&#039;&#039;getConfig()&#039;&#039;的时候，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; ，同时从jar中的&#039;&#039;config.yml&#039;&#039;加载相应的默认值。 需要注意的是，第一次使用之后再调用&#039;&#039;getConfig()&#039;&#039;只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的。除非你手动保存了，否则你对这个对象进行的修改并不会被保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;之后，对磁盘上的config.yml文件的修改不会被反映在这个对象里。如果在plugins文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你对一个 &#039;&#039;FileConfiguration&#039;&#039; 对象进行了修改操作，或者改变了某些 &#039;&#039;Lists&#039;&#039;，如果你想保存你的修改，让插件被卸载了这些数据也能得以保存的话，你可以把你修改的数据保存到硬盘里。保存文件到硬盘里，你可以借助 saveConfig 方法。这会重写硬盘里的那份配置文件。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件，用于存储配置信息或坚持额外的游戏信息你需要访问额外的配置文件，写下你自己的方法。仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 里的 getConfig，reloadConfig，saveConfig方法，下面是一个例子，如何编写你自己的方法来读取和保存自定义配置文件。由于这些配置文件，属于你的插件，你可以把这个方法在你的主类，这样你可以有相同的访问，你有config.yml 。你必须为每个YAML文件写的一套方法。这里的优势是，你可以使用都以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3422</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3422"/>
		<updated>2016-08-15T13:43:47Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 从磁盘中重载配置文件 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太过时了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin]的时候, 这个类也会继承JavaPlugin这个父类的的方法和属性。 在被继承的方法其中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次使用&#039;&#039;getConfig()&#039;&#039;的时候，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; ，同时从jar中的&#039;&#039;config.yml&#039;&#039;加载相应的默认值。 需要注意的是，第一次使用之后再调用&#039;&#039;getConfig()&#039;&#039;只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的。除非你手动保存了，否则你对这个对象进行的修改并不会被保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;之后，对磁盘上的config.yml文件的修改不会被反映在这个对象里。如果在plugins文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
If make any changes to the&#039;&#039;FileConfiguration&#039;&#039; with set methods, or mutate any &#039;&#039;Lists&#039;&#039;, you will need to save the changes to disk if you wish to keep these changes after the plugin is disabled. To save the file to disk invoke the saveConfig method for your plugin, it will overwrite the file already there. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
如果你怀疑用户在插件数据文件夹里修改了&#039;&#039;config.yml&#039;&#039;，并且那些数据并没有更新在内存里。你可以用 &#039;&#039;reloadConfig()&#039;&#039; 方法重新读取插件文件夹中最新的配置文件信息，来更新内存中的数据，但这会把你内存中保存的那份数据删除，这可能会让你丢失一部分数据。 &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件，用于存储配置信息或坚持额外的游戏信息你需要访问额外的配置文件，写下你自己的方法。仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 里的 getConfig，reloadConfig，saveConfig方法，下面是一个例子，如何编写你自己的方法来读取和保存自定义配置文件。由于这些配置文件，属于你的插件，你可以把这个方法在你的主类，这样你可以有相同的访问，你有config.yml 。你必须为每个YAML文件写的一套方法。这里的优势是，你可以使用都以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3421</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3421"/>
		<updated>2016-08-15T13:41:22Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 获得值 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太过时了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin]的时候, 这个类也会继承JavaPlugin这个父类的的方法和属性。 在被继承的方法其中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次使用&#039;&#039;getConfig()&#039;&#039;的时候，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; ，同时从jar中的&#039;&#039;config.yml&#039;&#039;加载相应的默认值。 需要注意的是，第一次使用之后再调用&#039;&#039;getConfig()&#039;&#039;只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的。除非你手动保存了，否则你对这个对象进行的修改并不会被保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;之后，对磁盘上的config.yml文件的修改不会被反映在这个对象里。如果在plugins文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
你可以使用一堆 getter 方法来获取配置文件中的值。这些 getter 方法你可以在[http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html 这里]找到。每个 getter 方法 takes a configuration path detailed above. 一般条件下你应该需要这些 getter 来获取值： &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String)   获取键对应的类型为boolean的值&lt;br /&gt;
*getInt(String)   获取键对应的类型为 int 的值&lt;br /&gt;
*getString(String)   获取键对应的类型为 String 的值&lt;br /&gt;
*getList(String)    获取键对应的类型为 List 的值&lt;br /&gt;
*getStringList(String)    &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
If make any changes to the&#039;&#039;FileConfiguration&#039;&#039; with set methods, or mutate any &#039;&#039;Lists&#039;&#039;, you will need to save the changes to disk if you wish to keep these changes after the plugin is disabled. To save the file to disk invoke the saveConfig method for your plugin, it will overwrite the file already there. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
If you suspect that users have made changes to the &#039;&#039;config.yml&#039;&#039; in the data folder, those changes are not reflected in memory. Invoke the &#039;&#039;reloadConfig()&#039;&#039; method of your plugin to load from the disk again. It will destroy all changes in memory. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件，用于存储配置信息或坚持额外的游戏信息你需要访问额外的配置文件，写下你自己的方法。仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 里的 getConfig，reloadConfig，saveConfig方法，下面是一个例子，如何编写你自己的方法来读取和保存自定义配置文件。由于这些配置文件，属于你的插件，你可以把这个方法在你的主类，这样你可以有相同的访问，你有config.yml 。你必须为每个YAML文件写的一套方法。这里的优势是，你可以使用都以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3420</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3420"/>
		<updated>2016-08-15T13:37:07Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 默认值 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太过时了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin]的时候, 这个类也会继承JavaPlugin这个父类的的方法和属性。 在被继承的方法其中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次使用&#039;&#039;getConfig()&#039;&#039;的时候，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; ，同时从jar中的&#039;&#039;config.yml&#039;&#039;加载相应的默认值。 需要注意的是，第一次使用之后再调用&#039;&#039;getConfig()&#039;&#039;只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的。除非你手动保存了，否则你对这个对象进行的修改并不会被保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;之后，对磁盘上的config.yml文件的修改不会被反映在这个对象里。如果在plugins文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的插件jar里应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;。当config.yml丢失或是不完整的情况出现时，将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取默认值。准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录。你应该在jar里的&#039;&#039;config.yml&#039;&#039;里准备一个完整的基本结构。你可以在插件主类的适当的地方使用 saveDefaultConfig() 方法把jar里的config.yml文件从jar中复制到插件配置文件夹里。&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你想动态的把一对键值作为默认值，你可以利用&#039;&#039;addDefault(String, Object)&#039;&#039; 方法和 &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; 方法。&lt;br /&gt;
&lt;br /&gt;
在某些情况下，如果你想附加一对新的键值给一个现存的 config.yml 文件，你可以通过通过传参布尔值 true给ConfigurationOptions对象里的copyDefaults方法来实现这一目的。 &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/你的插件/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
Reading values from the configuration involves invoking one of the many getter methods. A complete list of getters can be found [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html here]. Every getter method takes a configuration path detailed above. Some of the commonly used getter methods are as follows &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String) &lt;br /&gt;
*getInt(String) &lt;br /&gt;
*getString(String) &lt;br /&gt;
*getList(String) &lt;br /&gt;
*getStringList(String)&lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
If make any changes to the&#039;&#039;FileConfiguration&#039;&#039; with set methods, or mutate any &#039;&#039;Lists&#039;&#039;, you will need to save the changes to disk if you wish to keep these changes after the plugin is disabled. To save the file to disk invoke the saveConfig method for your plugin, it will overwrite the file already there. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
If you suspect that users have made changes to the &#039;&#039;config.yml&#039;&#039; in the data folder, those changes are not reflected in memory. Invoke the &#039;&#039;reloadConfig()&#039;&#039; method of your plugin to load from the disk again. It will destroy all changes in memory. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件，用于存储配置信息或坚持额外的游戏信息你需要访问额外的配置文件，写下你自己的方法。仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 里的 getConfig，reloadConfig，saveConfig方法，下面是一个例子，如何编写你自己的方法来读取和保存自定义配置文件。由于这些配置文件，属于你的插件，你可以把这个方法在你的主类，这样你可以有相同的访问，你有config.yml 。你必须为每个YAML文件写的一套方法。这里的优势是，你可以使用都以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3418</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3418"/>
		<updated>2016-08-15T12:56:51Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Configuration对象 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太过时了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== 配置文件对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin]的时候, 这个类也会继承JavaPlugin这个父类的的方法和属性。 在被继承的方法其中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次使用&#039;&#039;getConfig()&#039;&#039;的时候，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; ，同时从jar中的&#039;&#039;config.yml&#039;&#039;加载相应的默认值。 需要注意的是，第一次使用之后再调用&#039;&#039;getConfig()&#039;&#039;只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的。除非你手动保存了，否则你对这个对象进行的修改并不会被保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;之后，对磁盘上的config.yml文件的修改不会被反映在这个对象里。如果在plugins文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的jar中应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;.当出现config.yml丢失或是不完整的情况时,程序将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取值.准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录. The file should have the intended structure of your &#039;&#039;config.yml&#039;&#039;. This file can be copied as is into the datafolder by invoking saveDefaultConfig() on the Appropriate instance of JavaPlugin.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If dynamic key-value pairs are required as default values, they can added as defaults to the configuration with invocations of &#039;&#039;addDefault(String, Object)&#039;&#039; and &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; methods. &lt;br /&gt;
&lt;br /&gt;
In certain cases if you wish to append new key-value pairs to an existing config.yml you can set the copyDefaults to true for the ConfigurationOptions object. &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/YourPlugin/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
Reading values from the configuration involves invoking one of the many getter methods. A complete list of getters can be found [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html here]. Every getter method takes a configuration path detailed above. Some of the commonly used getter methods are as follows &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String) &lt;br /&gt;
*getInt(String) &lt;br /&gt;
*getString(String) &lt;br /&gt;
*getList(String) &lt;br /&gt;
*getStringList(String)&lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
If make any changes to the&#039;&#039;FileConfiguration&#039;&#039; with set methods, or mutate any &#039;&#039;Lists&#039;&#039;, you will need to save the changes to disk if you wish to keep these changes after the plugin is disabled. To save the file to disk invoke the saveConfig method for your plugin, it will overwrite the file already there. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
If you suspect that users have made changes to the &#039;&#039;config.yml&#039;&#039; in the data folder, those changes are not reflected in memory. Invoke the &#039;&#039;reloadConfig()&#039;&#039; method of your plugin to load from the disk again. It will destroy all changes in memory. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件，用于存储配置信息或坚持额外的游戏信息你需要访问额外的配置文件，写下你自己的方法。仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 里的 getConfig，reloadConfig，saveConfig方法，下面是一个例子，如何编写你自己的方法来读取和保存自定义配置文件。由于这些配置文件，属于你的插件，你可以把这个方法在你的主类，这样你可以有相同的访问，你有config.yml 。你必须为每个YAML文件写的一套方法。这里的优势是，你可以使用都以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3417</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=3417"/>
		<updated>2016-08-15T12:56:24Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Configuration对象 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太过时了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== Configuration对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
当你的插件主类继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin]的时候, 这个类也会继承JavaPlugin这个父类的的方法和属性。 在被继承的方法其中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类型的对象。这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; 。&lt;br /&gt;
&lt;br /&gt;
你的插件在第一次使用&#039;&#039;getConfig()&#039;&#039;的时候，会从你插件的jar文件中读取相应的&#039;&#039;config.yml&#039;&#039; ，同时从jar中的&#039;&#039;config.yml&#039;&#039;加载相应的默认值。 需要注意的是，第一次使用之后再调用&#039;&#039;getConfig()&#039;&#039;只会返回内存中保存的&#039;&#039;FileConfiguration&#039;&#039; 对象，而并非磁盘上保存的最新的。除非你手动保存了，否则你对这个对象进行的修改并不会被保存。同理，在第一次调用&#039;&#039;getConfig()&#039;&#039;之后，对磁盘上的config.yml文件的修改不会被反映在这个对象里。如果在plugins文件夹中对应的插件文件夹里不存在 &#039;&#039;config.yml&#039;&#039; ，这时会等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你在利用&#039;&#039;getConfig()&#039;&#039;的返回值，请不要把它赋给一个变量来使用}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 如果需要这样的话，一定要记住在重载配置文件后，再把 &#039;&#039;getConfig()&#039;&#039; 分配给那个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 保险起见，推荐你尽可能的直接用&#039;&#039;getConfig()&#039;&#039;，而不是将其分配给一个变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的jar中应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;.当出现config.yml丢失或是不完整的情况时,程序将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取值.准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录. The file should have the intended structure of your &#039;&#039;config.yml&#039;&#039;. This file can be copied as is into the datafolder by invoking saveDefaultConfig() on the Appropriate instance of JavaPlugin.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If dynamic key-value pairs are required as default values, they can added as defaults to the configuration with invocations of &#039;&#039;addDefault(String, Object)&#039;&#039; and &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; methods. &lt;br /&gt;
&lt;br /&gt;
In certain cases if you wish to append new key-value pairs to an existing config.yml you can set the copyDefaults to true for the ConfigurationOptions object. &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/YourPlugin/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
Reading values from the configuration involves invoking one of the many getter methods. A complete list of getters can be found [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html here]. Every getter method takes a configuration path detailed above. Some of the commonly used getter methods are as follows &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String) &lt;br /&gt;
*getInt(String) &lt;br /&gt;
*getString(String) &lt;br /&gt;
*getList(String) &lt;br /&gt;
*getStringList(String)&lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
If make any changes to the&#039;&#039;FileConfiguration&#039;&#039; with set methods, or mutate any &#039;&#039;Lists&#039;&#039;, you will need to save the changes to disk if you wish to keep these changes after the plugin is disabled. To save the file to disk invoke the saveConfig method for your plugin, it will overwrite the file already there. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
If you suspect that users have made changes to the &#039;&#039;config.yml&#039;&#039; in the data folder, those changes are not reflected in memory. Invoke the &#039;&#039;reloadConfig()&#039;&#039; method of your plugin to load from the disk again. It will destroy all changes in memory. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 高级  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== 菜单  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== 自定义配置文件  ===&lt;br /&gt;
&lt;br /&gt;
如果你需要额外的YAML文件，用于存储配置信息或坚持额外的游戏信息你需要访问额外的配置文件，写下你自己的方法。仿照 &amp;lt;big&amp;gt;JavaPlugin&amp;lt;/big&amp;gt; 里的 getConfig，reloadConfig，saveConfig方法，下面是一个例子，如何编写你自己的方法来读取和保存自定义配置文件。由于这些配置文件，属于你的插件，你可以把这个方法在你的主类，这样你可以有相同的访问，你有config.yml 。你必须为每个YAML文件写的一套方法。这里的优势是，你可以使用都以相同的方式设置为默认设置的方法config.yml 。另外，添加额外的方法可以保持较低的计数方法，并允许访问多个文件。&lt;br /&gt;
&lt;br /&gt;
==== JavaPlugin 镜像的实现 ====&lt;br /&gt;
javaplugin实现方法config.yml。插件需要实现它自己的方法来访问插件的配置文件。实现插件的方法后，就可以调用相同的上下文中的继承”getconfig() &#039;、&#039; reloadconfig() &#039;、&#039; saveconfig() &#039;，和&#039; savedefaultconfig()”方法javaplugin。以下可以做成一个类可以访问任何YAML文件。这样一个类可以在这里找到：&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/3174347 ClickHere]&lt;br /&gt;
&lt;br /&gt;
首先，您需要声明2个字段，并将其初始化为每个自定义配置文件空值。一个“&#039;fileconfiguration”对象和一个&amp;quot;file‘’的对象。file对象表示磁盘上的文件，和fileconfiguration代表配置的内容。&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实现重载 =====&lt;br /&gt;
然后，你写的方法负责从磁盘加载配置。它将加载文件和搜索位于jar里的默认customconfig.yml    。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现获取 =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== 实现保存 =====&lt;br /&gt;
&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 实施默认覆盖 =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 使用样例 ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules){&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2025</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2025"/>
		<updated>2016-01-17T10:28:37Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 介绍 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[分类:开发教程]]&lt;br /&gt;
{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
WorldEdit里的脚本可以让你不用Java和编译你的代码，方便地写一个操控世界的代码。这个脚本在WorldEdit中被称作&#039;&#039;CraftScripts&#039;&#039;，并且这一切都基于JavaScript和你的&#039;&#039;&#039;craftscripts/&#039;&#039;&#039; 文件. 使用基于WorldEdit写的脚本有这些优点:&lt;br /&gt;
&lt;br /&gt;
* 能够与WorldEdit的 undo（撤销）/redo（反撤销） 功能对接&lt;br /&gt;
* 能够使用WorldEdit来优化编辑方块的操作&lt;br /&gt;
* 允许使用WorldEdit强大的方块类型语法 //set sign:3|How|are|you?&lt;br /&gt;
* 能够轻易获取用户选择的区域空间&lt;br /&gt;
&lt;br /&gt;
此脚本需要通过Mozilla Rhino JavaScript在Java 6 或更高版本上运行。但是，如果WorldEdit的路径下有一个独立的Rhino版本，它会被优先使用。为了最大化的兼容性，请在Rhino的Java稳定版本上编写脚本。&lt;br /&gt;
脚本支持在v0.8时曾被移除。在v2.13后，脚本支持被重新加入。然而v0.8或更低的版本的脚本并不能兼容。&lt;br /&gt;
{{warning|请不要运行来自不受信任的来源的脚本。}}&lt;br /&gt;
&lt;br /&gt;
请查看 [http://docs.sk89q.com/ WorldEdit的API文档].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; 是一个 [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext] 的实例。&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; 是一个玩家的附件，是 [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer] 的实例。&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; 是一个String类型的Java数组&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. 这个对象会自动处理历史和方块放置的顺序。为了得到你自己的脚本的一个编辑样本，请使用：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. 这是因为方块包含各种各样的数据（如箱子方块里面的物品），如果只有方块类型ID的话，很多数据就会被丢失。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 与block是独立的，他并不能获取得到对象所在的世界。这意味着你可以传递一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 到任何地方并重复使用他。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;们记录着方块种类和数据。对于一些复杂的方块，比如说告示牌和箱子，你可以使用&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 就像这样&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
设置一个带有4号颜色的羊毛方块&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
标记这个是因为 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 是在&#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; 的命名空间,它必须被引入先. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; 有一个方块类型的列表. &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt;方法的第一个参数是一个表示在一个世界的位置的&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments由&amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt;变量传递。如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块的话，将会抛出一个异常。并且，抛出异常后如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，就不会再进行下去。&lt;br /&gt;
&lt;br /&gt;
== 配合使用Java包 ==&lt;br /&gt;
&lt;br /&gt;
你可以通过使用以下的语法来引入一个Java包:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
注意：如果你安装了Mozilla&#039;s Rhino库，则不需要&amp;quot;Packages.&amp;quot;前缀，但为了保持在默认的Java Rhino上运行的稳定性，&amp;quot;Packages.&amp;quot;前缀是推荐的。&lt;br /&gt;
当安装了以后，你可以使用所有Java的扩展库，以及所有WorldEdit的类。&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
例子: 迷宫生成脚本&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2024</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2024"/>
		<updated>2016-01-17T10:26:10Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 在方块上工作 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[分类:开发教程]]&lt;br /&gt;
{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
WorldEdit里的脚本可以让你不用Java和编译你的代码，方便地写一个操控世界的代码。这个脚本在WorldEdit中被称作&#039;&#039;CraftScripts&#039;&#039;，并且这一切都基于JavaScript和你的&#039;&#039;&#039;craftscripts/&#039;&#039;&#039; 文件. 使用基于WorldEdit写的脚本有这些优点:&lt;br /&gt;
&lt;br /&gt;
* 能够与WorldEdit的 undo（撤销）/redo（反撤销） 功能对接&lt;br /&gt;
* 能够使用WorldEdit来优化编辑方块的操作&lt;br /&gt;
* 允许使用WorldEdit强大的方块类型语法 //set sign:3|How|are|you?&lt;br /&gt;
* 能够轻易获取用户选择的区域空间&lt;br /&gt;
&lt;br /&gt;
此脚本需要通过Mozilla Rhino JavaScript在Java 6 或更高版本上运行。但是，如果WorldEdit的路径下有一个独立的Rhino版本，它会被优先使用。为了最大化的兼容性，请在Rhino的Java稳定版本上编写脚本。&lt;br /&gt;
脚本支持在v0.8时曾被移除。在v2.13后，脚本支持被重新加入。然而v0.8或更低的版本的脚本并不能兼容。&lt;br /&gt;
{{warning|请不要运行来自不受信任的来源的脚本。}}&lt;br /&gt;
&lt;br /&gt;
请查看 [http://docs.sk89q.com/ WorldEdit的API文档].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; 是一个String类型的Java数组&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. 这个对象会自动处理历史和方块放置的顺序。为了得到你自己的脚本的一个编辑样本，请使用：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. 这是因为方块包含各种各样的数据（如箱子方块里面的物品），如果只有方块类型ID的话，很多数据就会被丢失。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 与block是独立的，他并不能获取得到对象所在的世界。这意味着你可以传递一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 到任何地方并重复使用他。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;们记录着方块种类和数据。对于一些复杂的方块，比如说告示牌和箱子，你可以使用&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 就像这样&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
设置一个带有4号颜色的羊毛方块&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
标记这个是因为 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 是在&#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; 的命名空间,它必须被引入先. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; 有一个方块类型的列表. &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt;方法的第一个参数是一个表示在一个世界的位置的&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments由&amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt;变量传递。如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块的话，将会抛出一个异常。并且，抛出异常后如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，就不会再进行下去。&lt;br /&gt;
&lt;br /&gt;
== 配合使用Java包 ==&lt;br /&gt;
&lt;br /&gt;
你可以通过使用以下的语法来引入一个Java包:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
注意：如果你安装了Mozilla&#039;s Rhino库，则不需要&amp;quot;Packages.&amp;quot;前缀，但为了保持在默认的Java Rhino上运行的稳定性，&amp;quot;Packages.&amp;quot;前缀是推荐的。&lt;br /&gt;
当安装了以后，你可以使用所有Java的扩展库，以及所有WorldEdit的类。&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
例子: 迷宫生成脚本&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2023</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2023"/>
		<updated>2016-01-17T10:21:19Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 在方块上工作 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[分类:开发教程]]&lt;br /&gt;
{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
WorldEdit里的脚本可以让你不用Java和编译你的代码，方便地写一个操控世界的代码。这个脚本在WorldEdit中被称作&#039;&#039;CraftScripts&#039;&#039;，并且这一切都基于JavaScript和你的&#039;&#039;&#039;craftscripts/&#039;&#039;&#039; 文件. 使用基于WorldEdit写的脚本有这些优点:&lt;br /&gt;
&lt;br /&gt;
* 能够与WorldEdit的 undo（撤销）/redo（反撤销） 功能对接&lt;br /&gt;
* 能够使用WorldEdit来优化编辑方块的操作&lt;br /&gt;
* 允许使用WorldEdit强大的方块类型语法 //set sign:3|How|are|you?&lt;br /&gt;
* 能够轻易获取用户选择的区域空间&lt;br /&gt;
&lt;br /&gt;
此脚本需要通过Mozilla Rhino JavaScript在Java 6 或更高版本上运行。但是，如果WorldEdit的路径下有一个独立的Rhino版本，它会被优先使用。为了最大化的兼容性，请在Rhino的Java稳定版本上编写脚本。&lt;br /&gt;
脚本支持在v0.8时曾被移除。在v2.13后，脚本支持被重新加入。然而v0.8或更低的版本的脚本并不能兼容。&lt;br /&gt;
{{warning|请不要运行来自不受信任的来源的脚本。}}&lt;br /&gt;
&lt;br /&gt;
请查看 [http://docs.sk89q.com/ WorldEdit的API文档].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; 是一个String类型的Java数组&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. 这个对象会自动处理历史和方块放置的顺序。为了得到你自己的脚本的一个编辑样本，请使用：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. 这是因为方块包含各种各样的数据（如箱子方块里面的物品），如果只有方块类型ID的话，很多数据就会被丢失。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. 这意味着你可以传递一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 到任何地方并重复使用他。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
设置一个带有4号颜色的羊毛方块&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
标记这个是因为 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 是在&#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; 的命名空间,它必须被引入先. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; 有一个方块类型的列表. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments由&amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt;变量传递。如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块的话，将会抛出一个异常。并且，抛出异常后如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，就不会再进行下去。&lt;br /&gt;
&lt;br /&gt;
== 配合使用Java包 ==&lt;br /&gt;
&lt;br /&gt;
你可以通过使用以下的语法来引入一个Java包:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
注意：如果你安装了Mozilla&#039;s Rhino库，则不需要&amp;quot;Packages.&amp;quot;前缀，但为了保持在默认的Java Rhino上运行的稳定性，&amp;quot;Packages.&amp;quot;前缀是推荐的。&lt;br /&gt;
当安装了以后，你可以使用所有Java的扩展库，以及所有WorldEdit的类。&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
例子: 迷宫生成脚本&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2022</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2022"/>
		<updated>2016-01-17T10:20:50Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 处理参数 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[分类:开发教程]]&lt;br /&gt;
{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
WorldEdit里的脚本可以让你不用Java和编译你的代码，方便地写一个操控世界的代码。这个脚本在WorldEdit中被称作&#039;&#039;CraftScripts&#039;&#039;，并且这一切都基于JavaScript和你的&#039;&#039;&#039;craftscripts/&#039;&#039;&#039; 文件. 使用基于WorldEdit写的脚本有这些优点:&lt;br /&gt;
&lt;br /&gt;
* 能够与WorldEdit的 undo（撤销）/redo（反撤销） 功能对接&lt;br /&gt;
* 能够使用WorldEdit来优化编辑方块的操作&lt;br /&gt;
* 允许使用WorldEdit强大的方块类型语法 //set sign:3|How|are|you?&lt;br /&gt;
* 能够轻易获取用户选择的区域空间&lt;br /&gt;
&lt;br /&gt;
此脚本需要通过Mozilla Rhino JavaScript在Java 6 或更高版本上运行。但是，如果WorldEdit的路径下有一个独立的Rhino版本，它会被优先使用。为了最大化的兼容性，请在Rhino的Java稳定版本上编写脚本。&lt;br /&gt;
脚本支持在v0.8时曾被移除。在v2.13后，脚本支持被重新加入。然而v0.8或更低的版本的脚本并不能兼容。&lt;br /&gt;
{{warning|请不要运行来自不受信任的来源的脚本。}}&lt;br /&gt;
&lt;br /&gt;
请查看 [http://docs.sk89q.com/ WorldEdit的API文档].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; 是一个String类型的Java数组&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. 这个对象会自动处理历史和方块放置的顺序。为了得到你自己的脚本的一个编辑样本，请使用：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. 这是因为方块包含各种各样的数据（如箱子方块里面的物品），如果只有方块类型ID的话，很多数据就会被丢失。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. 这意味着你可以传递一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 到任何地方并重复使用他。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
标记这个是因为 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 是在&#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; 的命名空间,它必须被引入先. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; 有一个方块类型的列表. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments由&amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt;变量传递。如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块的话，将会抛出一个异常。并且，抛出异常后如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，就不会再进行下去。&lt;br /&gt;
&lt;br /&gt;
== 配合使用Java包 ==&lt;br /&gt;
&lt;br /&gt;
你可以通过使用以下的语法来引入一个Java包:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
注意：如果你安装了Mozilla&#039;s Rhino库，则不需要&amp;quot;Packages.&amp;quot;前缀，但为了保持在默认的Java Rhino上运行的稳定性，&amp;quot;Packages.&amp;quot;前缀是推荐的。&lt;br /&gt;
当安装了以后，你可以使用所有Java的扩展库，以及所有WorldEdit的类。&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
例子: 迷宫生成脚本&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2021</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=2021"/>
		<updated>2016-01-17T10:19:06Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 例程 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[分类:开发教程]]&lt;br /&gt;
{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
WorldEdit里的脚本可以让你不用Java和编译你的代码，方便地写一个操控世界的代码。这个脚本在WorldEdit中被称作&#039;&#039;CraftScripts&#039;&#039;，并且这一切都基于JavaScript和你的&#039;&#039;&#039;craftscripts/&#039;&#039;&#039; 文件. 使用基于WorldEdit写的脚本有这些优点:&lt;br /&gt;
&lt;br /&gt;
* 能够与WorldEdit的 undo（撤销）/redo（反撤销） 功能对接&lt;br /&gt;
* 能够使用WorldEdit来优化编辑方块的操作&lt;br /&gt;
* 允许使用WorldEdit强大的方块类型语法 //set sign:3|How|are|you?&lt;br /&gt;
* 能够轻易获取用户选择的区域空间&lt;br /&gt;
&lt;br /&gt;
此脚本需要通过Mozilla Rhino JavaScript在Java 6 或更高版本上运行。但是，如果WorldEdit的路径下有一个独立的Rhino版本，它会被优先使用。为了最大化的兼容性，请在Rhino的Java稳定版本上编写脚本。&lt;br /&gt;
脚本支持在v0.8时曾被移除。在v2.13后，脚本支持被重新加入。然而v0.8或更低的版本的脚本并不能兼容。&lt;br /&gt;
{{warning|请不要运行来自不受信任的来源的脚本。}}&lt;br /&gt;
&lt;br /&gt;
请查看 [http://docs.sk89q.com/ WorldEdit的API文档].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; 是一个String类型的Java数组&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. 这个对象会自动处理历史和方块放置的顺序。为了得到你自己的脚本的一个编辑样本，请使用：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. 这是因为方块包含各种各样的数据（如箱子方块里面的物品），如果只有方块类型ID的话，很多数据就会被丢失。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. 这意味着你可以传递一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 到任何地方并重复使用他。 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
标记这个是因为 &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; 是在&#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; 的命名空间,它必须被引入先. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; 有一个方块类型的列表. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments由&amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt;变量传递。如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块会怎么样？会抛出一个异常。并且如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，不再进行下去。&lt;br /&gt;
&lt;br /&gt;
== 配合使用Java包 ==&lt;br /&gt;
&lt;br /&gt;
你可以通过使用以下的语法来引入一个Java包:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
注意：如果你安装了Mozilla&#039;s Rhino库，则不需要&amp;quot;Packages.&amp;quot;前缀，但为了保持在默认的Java Rhino上运行的稳定性，&amp;quot;Packages.&amp;quot;前缀是推荐的。&lt;br /&gt;
当安装了以后，你可以使用所有Java的扩展库，以及所有WorldEdit的类。&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
例子: 迷宫生成脚本&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1804</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1804"/>
		<updated>2015-12-20T03:25:55Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​小汉化X2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
WorldEdit里的脚本可以让你不用Java和编译你的代码，方便地写一个操控世界的代码。这个脚本在WorldEdit中被称作&#039;&#039;CraftScripts&#039;&#039;，并且这一切都基于JavaScript and go into your &#039;&#039;&#039;craftscripts/&#039;&#039;&#039; 文件. 使用基于WorldEdit写的脚本有这些优点:&lt;br /&gt;
&lt;br /&gt;
* 能够与WorldEdit的 undo（撤销）/redo（反撤销） 功能对接&lt;br /&gt;
* 能够使用WorldEdit来优化编辑方块的操作&lt;br /&gt;
* Accept WorldEdit&#039;s powerful block type syntax //set sign:3|How|are|you?&lt;br /&gt;
* 能够轻易获取用户选择的区域空间&lt;br /&gt;
&lt;br /&gt;
Mozilla Rhino JavaScript engine bundled with Java 6 and above is used to evaluate the scripts. However, if an independent version of Rhino is found in WorldEdit&#039;s classpath, it will be used instead. For maximum portability, write scripts for Java&#039;s built-in version of Rhino. &lt;br /&gt;
&lt;br /&gt;
Scripting support was re-added in v2.13 after having been removed in v0.8. However, scripts written for v0.8 and below are not compatible. &lt;br /&gt;
&lt;br /&gt;
{{warning|Do not run scripts from untrusted sources.}}&lt;br /&gt;
&lt;br /&gt;
Check out [http://docs.sk89q.com/ WorldEdit&#039;s API documentation].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; 是一个String类型的Java数组&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. This object handles history and block placement order all automatically. To get an edit session for your own script, use:&lt;br /&gt;
&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. The reason is that blocks contain data (and in some cases, complex data like chest contents), and if only block types were passed around, a lot of data would be lost. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. That means that you can pass a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; around everywhere and reuse it. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/pre&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Note that because &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is in the &#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; namespace, it had to be imported first. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; has a list of block types. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments are passed in under the &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; variable. 如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块会怎么样？会抛出一个异常。并且如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，不再进行下去。&lt;br /&gt;
&lt;br /&gt;
== Working with Java packages ==&lt;br /&gt;
&lt;br /&gt;
To import a java package, you can use the following syntax:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
Tip: The &amp;quot;Packages.&amp;quot; prefix is not required if you installed Mozilla&#039;s Rhino library, but to keep portability with the default bundle of Rhino in Java, it is recommended that you use it.&lt;br /&gt;
&lt;br /&gt;
You have access to all of Java&#039;s vast library as it is installed. WorldEdit&#039;s classes are also fully available.&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example: Maze generation script&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1803</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1803"/>
		<updated>2015-12-20T03:24:52Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​小小的汉化了一下&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
WorldEdit里的脚本可以让你不用Java和编译你的代码，方便地写一个操控世界的代码。这个脚本在WorldEdit中被称作&#039;&#039;CraftScripts&#039;&#039;，并且这一切都基于JavaScript and go into your &#039;&#039;&#039;craftscripts/&#039;&#039;&#039; 文件. 使用基于WorldEdit写的脚本有这些优点:&lt;br /&gt;
&lt;br /&gt;
* 能够与WorldEdit的 undo（撤销）/redo（反撤销） 功能对接&lt;br /&gt;
* 能够使用WorldEdit来优化编辑方块的操作&lt;br /&gt;
* Accept WorldEdit&#039;s powerful block type syntax //set sign:3|How|are|you?&lt;br /&gt;
* 能够轻易获取用户选择的区域空间&lt;br /&gt;
&lt;br /&gt;
Mozilla Rhino JavaScript engine bundled with Java 6 and above is used to evaluate the scripts. However, if an independent version of Rhino is found in WorldEdit&#039;s classpath, it will be used instead. For maximum portability, write scripts for Java&#039;s built-in version of Rhino. &lt;br /&gt;
&lt;br /&gt;
Scripting support was re-added in v2.13 after having been removed in v0.8. However, scripts written for v0.8 and below are not compatible. &lt;br /&gt;
&lt;br /&gt;
{{warning|Do not run scripts from untrusted sources.}}&lt;br /&gt;
&lt;br /&gt;
Check out [http://docs.sk89q.com/ WorldEdit&#039;s API documentation].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; is a Java array of arguments as strings&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. This object handles history and block placement order all automatically. To get an edit session for your own script, use:&lt;br /&gt;
&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. The reason is that blocks contain data (and in some cases, complex data like chest contents), and if only block types were passed around, a lot of data would be lost. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. That means that you can pass a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; around everywhere and reuse it. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/pre&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Note that because &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is in the &#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; namespace, it had to be imported first. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; has a list of block types. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments are passed in under the &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; variable. 如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块会怎么样？会抛出一个异常。并且如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，不再进行下去。&lt;br /&gt;
&lt;br /&gt;
== Working with Java packages ==&lt;br /&gt;
&lt;br /&gt;
To import a java package, you can use the following syntax:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
Tip: The &amp;quot;Packages.&amp;quot; prefix is not required if you installed Mozilla&#039;s Rhino library, but to keep portability with the default bundle of Rhino in Java, it is recommended that you use it.&lt;br /&gt;
&lt;br /&gt;
You have access to all of Java&#039;s vast library as it is installed. WorldEdit&#039;s classes are also fully available.&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example: Maze generation script&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1802</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1802"/>
		<updated>2015-12-20T03:16:07Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 处理参数 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
Scripting in WorldEdit allows you to write world manipulation code without having to learn Java or compile your code. Scripts, called &#039;&#039;CraftScripts&#039;&#039; in WorldEdit, and are written in JavaScript and go into your &#039;&#039;&#039;craftscripts/&#039;&#039;&#039; directory. The advantages of writing scripts with WorldEdit are:&lt;br /&gt;
&lt;br /&gt;
* Hook right into WorldEdit&#039;s undo/redo system&lt;br /&gt;
* Use WorldEdit&#039;s block place prioritization&lt;br /&gt;
* Accept WorldEdit&#039;s powerful block type syntax //set sign:3|How|are|you?&lt;br /&gt;
* Get the region selected by the user&lt;br /&gt;
&lt;br /&gt;
The Mozilla Rhino JavaScript engine bundled with Java 6 and above is used to evaluate the scripts. However, if an independent version of Rhino is found in WorldEdit&#039;s classpath, it will be used instead. For maximum portability, write scripts for Java&#039;s built-in version of Rhino. &lt;br /&gt;
&lt;br /&gt;
Scripting support was re-added in v2.13 after having been removed in v0.8. However, scripts written for v0.8 and below are not compatible. &lt;br /&gt;
&lt;br /&gt;
{{warning|Do not run scripts from untrusted sources.}}&lt;br /&gt;
&lt;br /&gt;
Check out [http://docs.sk89q.com/ WorldEdit&#039;s API documentation].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; is a Java array of arguments as strings&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. This object handles history and block placement order all automatically. To get an edit session for your own script, use:&lt;br /&gt;
&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. The reason is that blocks contain data (and in some cases, complex data like chest contents), and if only block types were passed around, a lot of data would be lost. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. That means that you can pass a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; around everywhere and reuse it. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/pre&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Note that because &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is in the &#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; namespace, it had to be imported first. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; has a list of block types. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments are passed in under the &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; variable. 如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:检查参数&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. 这个方法可以检查方块黑名单, however. 如果你需要一个方块去, 比如说, 替换, 你可以 ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果用户输入的是一个无效的方块会怎么样？会抛出一个异常。并且如果你没有去捕捉他，用户就会被提示这个异常，然后这个脚本就会被终止，不再进行下去。&lt;br /&gt;
&lt;br /&gt;
== Working with Java packages ==&lt;br /&gt;
&lt;br /&gt;
To import a java package, you can use the following syntax:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
Tip: The &amp;quot;Packages.&amp;quot; prefix is not required if you installed Mozilla&#039;s Rhino library, but to keep portability with the default bundle of Rhino in Java, it is recommended that you use it.&lt;br /&gt;
&lt;br /&gt;
You have access to all of Java&#039;s vast library as it is installed. WorldEdit&#039;s classes are also fully available.&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example: Maze generation script&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1801</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1801"/>
		<updated>2015-12-20T03:11:57Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Example scripts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
Scripting in WorldEdit allows you to write world manipulation code without having to learn Java or compile your code. Scripts, called &#039;&#039;CraftScripts&#039;&#039; in WorldEdit, and are written in JavaScript and go into your &#039;&#039;&#039;craftscripts/&#039;&#039;&#039; directory. The advantages of writing scripts with WorldEdit are:&lt;br /&gt;
&lt;br /&gt;
* Hook right into WorldEdit&#039;s undo/redo system&lt;br /&gt;
* Use WorldEdit&#039;s block place prioritization&lt;br /&gt;
* Accept WorldEdit&#039;s powerful block type syntax //set sign:3|How|are|you?&lt;br /&gt;
* Get the region selected by the user&lt;br /&gt;
&lt;br /&gt;
The Mozilla Rhino JavaScript engine bundled with Java 6 and above is used to evaluate the scripts. However, if an independent version of Rhino is found in WorldEdit&#039;s classpath, it will be used instead. For maximum portability, write scripts for Java&#039;s built-in version of Rhino. &lt;br /&gt;
&lt;br /&gt;
Scripting support was re-added in v2.13 after having been removed in v0.8. However, scripts written for v0.8 and below are not compatible. &lt;br /&gt;
&lt;br /&gt;
{{warning|Do not run scripts from untrusted sources.}}&lt;br /&gt;
&lt;br /&gt;
Check out [http://docs.sk89q.com/ WorldEdit&#039;s API documentation].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; is a Java array of arguments as strings&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. This object handles history and block placement order all automatically. To get an edit session for your own script, use:&lt;br /&gt;
&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. The reason is that blocks contain data (and in some cases, complex data like chest contents), and if only block types were passed around, a lot of data would be lost. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. That means that you can pass a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; around everywhere and reuse it. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/pre&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Note that because &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is in the &#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; namespace, it had to be imported first. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; has a list of block types. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments are passed in under the &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; variable. 如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:Checking arguments&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. That method checks the block blacklist, however. If you need a block for, for example, replacing, you can ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What happens if the user inputs an invalid block? An exception will be raised and if you don&#039;t catch it, the user will be informed about their error and your script will be halted.&lt;br /&gt;
&lt;br /&gt;
== Working with Java packages ==&lt;br /&gt;
&lt;br /&gt;
To import a java package, you can use the following syntax:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
Tip: The &amp;quot;Packages.&amp;quot; prefix is not required if you installed Mozilla&#039;s Rhino library, but to keep portability with the default bundle of Rhino in Java, it is recommended that you use it.&lt;br /&gt;
&lt;br /&gt;
You have access to all of Java&#039;s vast library as it is installed. WorldEdit&#039;s classes are also fully available.&lt;br /&gt;
&lt;br /&gt;
== 例程 ==&lt;br /&gt;
&lt;br /&gt;
例子:设置附近的发射器装满箭&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example: Maze generation script&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1800</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1800"/>
		<updated>2015-12-20T03:09:59Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Processing arguments */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
Scripting in WorldEdit allows you to write world manipulation code without having to learn Java or compile your code. Scripts, called &#039;&#039;CraftScripts&#039;&#039; in WorldEdit, and are written in JavaScript and go into your &#039;&#039;&#039;craftscripts/&#039;&#039;&#039; directory. The advantages of writing scripts with WorldEdit are:&lt;br /&gt;
&lt;br /&gt;
* Hook right into WorldEdit&#039;s undo/redo system&lt;br /&gt;
* Use WorldEdit&#039;s block place prioritization&lt;br /&gt;
* Accept WorldEdit&#039;s powerful block type syntax //set sign:3|How|are|you?&lt;br /&gt;
* Get the region selected by the user&lt;br /&gt;
&lt;br /&gt;
The Mozilla Rhino JavaScript engine bundled with Java 6 and above is used to evaluate the scripts. However, if an independent version of Rhino is found in WorldEdit&#039;s classpath, it will be used instead. For maximum portability, write scripts for Java&#039;s built-in version of Rhino. &lt;br /&gt;
&lt;br /&gt;
Scripting support was re-added in v2.13 after having been removed in v0.8. However, scripts written for v0.8 and below are not compatible. &lt;br /&gt;
&lt;br /&gt;
{{warning|Do not run scripts from untrusted sources.}}&lt;br /&gt;
&lt;br /&gt;
Check out [http://docs.sk89q.com/ WorldEdit&#039;s API documentation].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; is a Java array of arguments as strings&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. This object handles history and block placement order all automatically. To get an edit session for your own script, use:&lt;br /&gt;
&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. The reason is that blocks contain data (and in some cases, complex data like chest contents), and if only block types were passed around, a lot of data would be lost. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. That means that you can pass a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; around everywhere and reuse it. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/pre&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Note that because &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is in the &#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; namespace, it had to be imported first. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; has a list of block types. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 处理参数 ==&lt;br /&gt;
&lt;br /&gt;
Arguments are passed in under the &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; variable. 如果你需要去检查用户提供的参数是不是正确的，你可以用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:Checking arguments&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你要去处理一个方块名字使其成为&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, 你可以使用 &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. That method checks the block blacklist, however. If you need a block for, for example, replacing, you can ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
举个例子:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What happens if the user inputs an invalid block? An exception will be raised and if you don&#039;t catch it, the user will be informed about their error and your script will be halted.&lt;br /&gt;
&lt;br /&gt;
== Working with Java packages ==&lt;br /&gt;
&lt;br /&gt;
To import a java package, you can use the following syntax:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
Tip: The &amp;quot;Packages.&amp;quot; prefix is not required if you installed Mozilla&#039;s Rhino library, but to keep portability with the default bundle of Rhino in Java, it is recommended that you use it.&lt;br /&gt;
&lt;br /&gt;
You have access to all of Java&#039;s vast library as it is installed. WorldEdit&#039;s classes are also fully available.&lt;br /&gt;
&lt;br /&gt;
== Example scripts ==&lt;br /&gt;
&lt;br /&gt;
Example:Set nearby dispensers to be filled with arrows&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example: Maze generation script&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1799</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1799"/>
		<updated>2015-12-20T03:06:14Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Working with blocks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
Scripting in WorldEdit allows you to write world manipulation code without having to learn Java or compile your code. Scripts, called &#039;&#039;CraftScripts&#039;&#039; in WorldEdit, and are written in JavaScript and go into your &#039;&#039;&#039;craftscripts/&#039;&#039;&#039; directory. The advantages of writing scripts with WorldEdit are:&lt;br /&gt;
&lt;br /&gt;
* Hook right into WorldEdit&#039;s undo/redo system&lt;br /&gt;
* Use WorldEdit&#039;s block place prioritization&lt;br /&gt;
* Accept WorldEdit&#039;s powerful block type syntax //set sign:3|How|are|you?&lt;br /&gt;
* Get the region selected by the user&lt;br /&gt;
&lt;br /&gt;
The Mozilla Rhino JavaScript engine bundled with Java 6 and above is used to evaluate the scripts. However, if an independent version of Rhino is found in WorldEdit&#039;s classpath, it will be used instead. For maximum portability, write scripts for Java&#039;s built-in version of Rhino. &lt;br /&gt;
&lt;br /&gt;
Scripting support was re-added in v2.13 after having been removed in v0.8. However, scripts written for v0.8 and below are not compatible. &lt;br /&gt;
&lt;br /&gt;
{{warning|Do not run scripts from untrusted sources.}}&lt;br /&gt;
&lt;br /&gt;
Check out [http://docs.sk89q.com/ WorldEdit&#039;s API documentation].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; is a Java array of arguments as strings&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== 在方块上工作 ==&lt;br /&gt;
&lt;br /&gt;
在WorldEdit中所有的方块编辑操作都基于一个 &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. This object handles history and block placement order all automatically. To get an edit session for your own script, use:&lt;br /&gt;
&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
任何时候你调用这个方法，你都会得到一个新的 &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, 所以一定要保持一个左右。要设置一个方块，而不是给一个方块的物品ID。你给一个&amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. The reason is that blocks contain data (and in some cases, complex data like chest contents), and if only block types were passed around, a lot of data would be lost. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. That means that you can pass a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; around everywhere and reuse it. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
举个例子:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/pre&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Note that because &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is in the &#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; namespace, it had to be imported first. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; has a list of block types. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
想要得到方块，使用基于&amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt;函数。你将也能得到一个&amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== Processing arguments ==&lt;br /&gt;
&lt;br /&gt;
Arguments are passed in under the &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; variable. If you need to check whether the right number of arguments was provided by the user, you can use &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Exmaple:Checking arguments&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to resolve a block name to a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, you can use &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. That method checks the block blacklist, however. If you need a block for, for example, replacing, you can ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
Example:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What happens if the user inputs an invalid block? An exception will be raised and if you don&#039;t catch it, the user will be informed about their error and your script will be halted.&lt;br /&gt;
&lt;br /&gt;
== Working with Java packages ==&lt;br /&gt;
&lt;br /&gt;
To import a java package, you can use the following syntax:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
Tip: The &amp;quot;Packages.&amp;quot; prefix is not required if you installed Mozilla&#039;s Rhino library, but to keep portability with the default bundle of Rhino in Java, it is recommended that you use it.&lt;br /&gt;
&lt;br /&gt;
You have access to all of Java&#039;s vast library as it is installed. WorldEdit&#039;s classes are also fully available.&lt;br /&gt;
&lt;br /&gt;
== Example scripts ==&lt;br /&gt;
&lt;br /&gt;
Example:Set nearby dispensers to be filled with arrows&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example: Maze generation script&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1798</id>
		<title>WorldEdit/CraftScript</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=WorldEdit/CraftScript&amp;diff=1798"/>
		<updated>2015-12-20T02:59:50Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
&lt;br /&gt;
Scripting in WorldEdit allows you to write world manipulation code without having to learn Java or compile your code. Scripts, called &#039;&#039;CraftScripts&#039;&#039; in WorldEdit, and are written in JavaScript and go into your &#039;&#039;&#039;craftscripts/&#039;&#039;&#039; directory. The advantages of writing scripts with WorldEdit are:&lt;br /&gt;
&lt;br /&gt;
* Hook right into WorldEdit&#039;s undo/redo system&lt;br /&gt;
* Use WorldEdit&#039;s block place prioritization&lt;br /&gt;
* Accept WorldEdit&#039;s powerful block type syntax //set sign:3|How|are|you?&lt;br /&gt;
* Get the region selected by the user&lt;br /&gt;
&lt;br /&gt;
The Mozilla Rhino JavaScript engine bundled with Java 6 and above is used to evaluate the scripts. However, if an independent version of Rhino is found in WorldEdit&#039;s classpath, it will be used instead. For maximum portability, write scripts for Java&#039;s built-in version of Rhino. &lt;br /&gt;
&lt;br /&gt;
Scripting support was re-added in v2.13 after having been removed in v0.8. However, scripts written for v0.8 and below are not compatible. &lt;br /&gt;
&lt;br /&gt;
{{warning|Do not run scripts from untrusted sources.}}&lt;br /&gt;
&lt;br /&gt;
Check out [http://docs.sk89q.com/ WorldEdit&#039;s API documentation].&lt;br /&gt;
&lt;br /&gt;
== 介绍 ==&lt;br /&gt;
&lt;br /&gt;
脚本有以下在他们的命名空间中的三个变量：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html CraftScriptContext]&lt;br /&gt;
* &amp;lt;code&amp;gt;player&amp;lt;/code&amp;gt; is a copy of the player, an instance of [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/LocalPlayer.html LocalPlayer]&lt;br /&gt;
* &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; is a Java array of arguments as strings&lt;br /&gt;
&lt;br /&gt;
想要运行一个脚本，请使用指令 /cs YourScript.js&lt;br /&gt;
&lt;br /&gt;
== Working with blocks ==&lt;br /&gt;
&lt;br /&gt;
All block editing in WorldEdit is done through an &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/EditSession.html EditSession]&amp;lt;/code&amp;gt;. This object handles history and block placement order all automatically. To get an edit session for your own script, use:&lt;br /&gt;
&lt;br /&gt;
 var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
Every time you call that method, you will get a new &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;, so be sure to keep one around. To set blocks, rather than giving a block type number, you give a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BaseBlock.html BaseBlock]&amp;lt;/code&amp;gt;. The reason is that blocks contain data (and in some cases, complex data like chest contents), and if only block types were passed around, a lot of data would be lost. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is an independent representation of a block; it doesn&#039;t know where it is in the world. That means that you can pass a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; around everywhere and reuse it. &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;s remember block type and block data. For more complex blocks such as signs and chests, you will use a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; derivative such as &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/SignBlock.html SignBlock]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Example:&amp;lt;br/&amp;gt;&lt;br /&gt;
Setting a cloth block with color value 4&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
sess.setBlock(player.getBlockOn(), new BaseBlock(BlockID.CLOTH, 4));&amp;lt;/pre&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Note that because &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; is in the &#039;&#039;com.sk89q.worldedit.blocks&#039;&#039; namespace, it had to be imported first. &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/blocks/BlockID.html BlockID]&amp;lt;/code&amp;gt; has a list of block types. The first argument for &amp;lt;code&amp;gt;setBlock()&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/Vector.html Vector]&amp;lt;/code&amp;gt; indicating the position in the world.&lt;br /&gt;
&lt;br /&gt;
To get blocks, use &amp;lt;code&amp;gt;getBlock()&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;EditSession&amp;lt;/code&amp;gt;. You&#039;ll get back a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt; too.&lt;br /&gt;
&lt;br /&gt;
== Processing arguments ==&lt;br /&gt;
&lt;br /&gt;
Arguments are passed in under the &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; variable. If you need to check whether the right number of arguments was provided by the user, you can use &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#checkArgs%28int,%20int,%20java.lang.String%29 checkArgs]()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Exmaple:Checking arguments&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to resolve a block name to a &amp;lt;code&amp;gt;BaseBlock&amp;lt;/code&amp;gt;, you can use &amp;lt;code&amp;gt;CraftScriptContext.[http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String%29 getBlock]()&amp;lt;/code&amp;gt;. That method checks the block blacklist, however. If you need a block for, for example, replacing, you can ignore the blacklist by [http://docs.sk89q.com/worldedit/apidocs/com/sk89q/worldedit/scripting/CraftScriptContext.html#getBlock%28java.lang.String,%20boolean%29 adding a boolean &amp;quot;true&amp;quot; for a third parameter].&lt;br /&gt;
&lt;br /&gt;
Example:Getting the passed in block&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What happens if the user inputs an invalid block? An exception will be raised and if you don&#039;t catch it, the user will be informed about their error and your script will be halted.&lt;br /&gt;
&lt;br /&gt;
== Working with Java packages ==&lt;br /&gt;
&lt;br /&gt;
To import a java package, you can use the following syntax:&lt;br /&gt;
&lt;br /&gt;
 importPackage(Packages.package.name.here);&lt;br /&gt;
&lt;br /&gt;
Tip: The &amp;quot;Packages.&amp;quot; prefix is not required if you installed Mozilla&#039;s Rhino library, but to keep portability with the default bundle of Rhino in Java, it is recommended that you use it.&lt;br /&gt;
&lt;br /&gt;
You have access to all of Java&#039;s vast library as it is installed. WorldEdit&#039;s classes are also fully available.&lt;br /&gt;
&lt;br /&gt;
== Example scripts ==&lt;br /&gt;
&lt;br /&gt;
Example:Set nearby dispensers to be filled with arrows&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
var session = context.remember();&lt;br /&gt;
var origin = player.getPosition();&lt;br /&gt;
&lt;br /&gt;
var arrows = new BaseItemStack(262, 64);&lt;br /&gt;
&lt;br /&gt;
var items = [arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows,&lt;br /&gt;
             arrows, arrows, arrows]&lt;br /&gt;
&lt;br /&gt;
for (var x = -4; x &amp;lt;= 4; x++) {&lt;br /&gt;
    for (var y = -4; y &amp;lt;= 4; y++) {&lt;br /&gt;
        for (var z = -4; z &amp;lt;= 4; z++) {&lt;br /&gt;
            var pt = origin.add(x, y, z);&lt;br /&gt;
            var id = session.getBlockType(pt);&lt;br /&gt;
            &lt;br /&gt;
            if (id == BlockID.DISPENSER) {&lt;br /&gt;
                var block = session.getBlock(pt);&lt;br /&gt;
                block.setItems(items);&lt;br /&gt;
                session.setBlock(pt, block);&lt;br /&gt;
                &lt;br /&gt;
                player.print(&amp;quot;Arrows set @ &amp;quot; + pt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example: Maze generation script&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit);&lt;br /&gt;
importPackage(Packages.com.sk89q.worldedit.blocks);&lt;br /&gt;
&lt;br /&gt;
context.checkArgs(1, 3, &amp;quot;&amp;lt;block&amp;gt; [width] [height]&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var sess = context.remember();&lt;br /&gt;
&lt;br /&gt;
// This may throw an exception that is caught by the script processor&lt;br /&gt;
var block = context.getBlock(argv[1]);&lt;br /&gt;
var w = argv.length &amp;gt; 2 ? parseInt(argv[2]) : 5;&lt;br /&gt;
var h = argv.length &amp;gt; 3 ? parseInt(argv[3]) : 5;&lt;br /&gt;
&lt;br /&gt;
function id(x, y) {&lt;br /&gt;
    return y * (w + 1) + x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $x(i) {&lt;br /&gt;
    return i % (w + 1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function $y(i) {&lt;br /&gt;
    return Math.floor(i / (w + 1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function shuffle(arr) {&lt;br /&gt;
    var i = arr.length;&lt;br /&gt;
    if (i == 0) return false;&lt;br /&gt;
    while (--i) {&lt;br /&gt;
        var j = Math.floor(Math.random() * (i + 1));&lt;br /&gt;
        var tempi = arr[i];&lt;br /&gt;
        var tempj = arr[j];&lt;br /&gt;
        arr[i] = tempj;&lt;br /&gt;
        arr[j] = tempi;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var stack = [];&lt;br /&gt;
var visited = {};&lt;br /&gt;
var noWallLeft = new Array(w * h);&lt;br /&gt;
var noWallAbove = new Array(w * h);&lt;br /&gt;
var current = 0;&lt;br /&gt;
&lt;br /&gt;
stack.push(id(0, 0))&lt;br /&gt;
&lt;br /&gt;
while (stack.length &amp;gt; 0) {&lt;br /&gt;
    var cell = stack.pop();&lt;br /&gt;
    var x = $x(cell), y = $y(cell);&lt;br /&gt;
    visited[cell] = true;&lt;br /&gt;
    &lt;br /&gt;
    var neighbors = []&lt;br /&gt;
    &lt;br /&gt;
    if (x &amp;gt; 0) neighbors.push(id(x - 1, y));&lt;br /&gt;
    if (x &amp;lt; w - 1) neighbors.push(id(x + 1, y));&lt;br /&gt;
    if (y &amp;gt; 0) neighbors.push(id(x, y - 1));&lt;br /&gt;
    if (y &amp;lt; h - 1) neighbors.push(id(x, y + 1));&lt;br /&gt;
    &lt;br /&gt;
    shuffle(neighbors);&lt;br /&gt;
    &lt;br /&gt;
    while (neighbors.length &amp;gt; 0) {&lt;br /&gt;
        var neighbor = neighbors.pop();&lt;br /&gt;
        var nx = $x(neighbor), ny = $y(neighbor);&lt;br /&gt;
        &lt;br /&gt;
        if (visited[neighbor] != true) {&lt;br /&gt;
            stack.push(cell);&lt;br /&gt;
            &lt;br /&gt;
            if (y == ny) {&lt;br /&gt;
                if (nx &amp;lt; x) {&lt;br /&gt;
                    noWallLeft[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallLeft[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                if (ny &amp;lt; y) {&lt;br /&gt;
                    noWallAbove[cell] = true;&lt;br /&gt;
                } else {&lt;br /&gt;
                    noWallAbove[neighbor] = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            stack.push(neighbor);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var origin = player.getBlockIn();&lt;br /&gt;
&lt;br /&gt;
for (var y = 0; y &amp;lt;= h; y++) {&lt;br /&gt;
    for (var x = 0; x &amp;lt;= w; x++) {&lt;br /&gt;
        var cell = id(x, y)&lt;br /&gt;
        if (!noWallLeft[cell] &amp;amp;&amp;amp; y &amp;lt; h) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 0, y * 2), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2 - 1, 1, y * 2), block);&lt;br /&gt;
        }&lt;br /&gt;
        if (!noWallAbove[cell] &amp;amp;&amp;amp; x &amp;lt; w) {&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 0, y * 2 - 1), block);&lt;br /&gt;
            sess.setBlock(origin.add(x * 2, 1, y * 2 - 1), block);&lt;br /&gt;
        }&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 0, y * 2 - 1), block);&lt;br /&gt;
        sess.setBlock(origin.add(x * 2 - 1, 1, y * 2 - 1), block);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Developing_a_permissions_plugin&amp;diff=1416</id>
		<title>Developing a permissions plugin</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Developing_a_permissions_plugin&amp;diff=1416"/>
		<updated>2015-09-03T12:33:32Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​汉化引言部分&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Developing a permissions plugin/en|点击此处查看原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
== 引言  ==&lt;br /&gt;
&lt;br /&gt;
这篇教程将会带领你了解有关怎么基于新的BukkitPermissionsAPI给你插件创建权限。&lt;br /&gt;
&lt;br /&gt;
=== 前提  ===&lt;br /&gt;
&lt;br /&gt;
这篇教程需要你掌握很好的Java知识和一些插件开发知识。这篇教程只会涵盖有关Bukkit Permissions API的各种细节。&lt;br /&gt;
&lt;br /&gt;
== Permissible  ==&lt;br /&gt;
&lt;br /&gt;
Everything that can have permissions set must inherit the &#039;&#039;Permissible&#039;&#039; class. Currently these only include the &#039;&#039;Player&#039;&#039; and &#039;&#039;CommandSender&#039;&#039; classes, however in theory anything could have permissions. For the future of this tutorial we will mention the player rather than a permissible object.&lt;br /&gt;
&lt;br /&gt;
== PermissionAttachments  ==&lt;br /&gt;
&lt;br /&gt;
=== What is a PermissionAttachment?  ===&lt;br /&gt;
&lt;br /&gt;
A permission attachment is a way for a plugin to manage a player&#039;s permissions, and even better, allows multiple plugins to manage the player&#039;s permissions without interference, unless of course they want to set the same permission. &lt;br /&gt;
&lt;br /&gt;
=== How to create and remove them  ===&lt;br /&gt;
&lt;br /&gt;
Every player must first be &amp;quot;attached&amp;quot; to the plugin so that it can be managed. To create a PermissionAttachment, use the following: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;PermissionAttachment attachment = player.addAttachment(plugin);&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
You can even create an attachment that will only last for a limited number of ticks: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;PermissionAttachment attachment = player.addAttachment(plugin,ticks);&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This attachment needs to be stored somewhere though. The best solution is to create a &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;HashMap&amp;lt;UUID,PermissionAttachment&amp;gt;&amp;lt;/source&amp;gt;&amp;amp;nbsp;and store it in there with the player&#039;s Unique ID. That will later allow you to manage a particular player&#039;s permissions via their Unique ID, and remove the attachment when it&#039;s no longer needed. The best places to remove the attachment are in the &#039;&#039;onPlayerQuit&#039;&#039; event, &#039;&#039;onPlayerKick&#039;&#039; event, and remove them all in the plugin&#039;s &#039;&#039;onDisable&#039;&#039; event. Removing the attachment is as follows: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;player.removeAttachment(attachment);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning permissions  ==&lt;br /&gt;
&lt;br /&gt;
There are two main ways to set a player&#039;s permission: &lt;br /&gt;
&lt;br /&gt;
#Via a string. This is probably the easiest method, and the one which I will explain first. &lt;br /&gt;
#Via a permission Object. This is slightly more complicated, however it works in exactly the same way as the first internally, so I won&#039;t bother going over this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
To set a player&#039;s permission you firstly have to get the PermissionAttachment you created earlier from the HashMap. Once you have this then setting a player&#039;s permissions is easy via the following: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;attachment.setPermission(permissionName,permissionValue);&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The permissionName is a String, and permissionValue is a boolean. You can later unset a player&#039;s permission (different to setting it to false) through the following: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;attachment.unsetPermission(permissionName);&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The reason you may want to unset a player&#039;s permission is if you want to allow a different permissions plugin to set it. &lt;br /&gt;
&lt;br /&gt;
== Conclusion  ==&lt;br /&gt;
&lt;br /&gt;
That&#039;s about all there is to know about writing your own permissions plugin. The actual implementation of the plugin however is up to you. I can however provide a few examples: &lt;br /&gt;
&lt;br /&gt;
*An RPG plugin that gives player&#039;s permissions based on their XP &lt;br /&gt;
*A forum bridge that links groups/permissions from a forum such as phpBB to bukkit&#039;s permissions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1413</id>
		<title>Bukkit/插件开发教程</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1413"/>
		<updated>2015-09-03T09:04:19Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​更正不妥当的翻译&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
{{模板:待完善}}&lt;br /&gt;
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]&lt;br /&gt;
== 介绍  ==&lt;br /&gt;
&lt;br /&gt;
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.&lt;br /&gt;
&lt;br /&gt;
== 学习Java  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;理解这些教程需要具备Java这门编程语言的基本知识.&#039;&#039;&#039;如果你刚开始学习Java或者需要重温一下相关知识，下面有一个并不完整的网站列表可供参考.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;[http://docs.oracle.com/javase/tutorial/ Oracle&#039;s Java Tutorials]&amp;lt;/big&amp;gt; - 官方教程&lt;br /&gt;
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程&lt;br /&gt;
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程&lt;br /&gt;
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书&lt;br /&gt;
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.&lt;br /&gt;
&lt;br /&gt;
== 开发环境  ==&lt;br /&gt;
&lt;br /&gt;
在编写一个插件前 (或学习Java前) 你需要配置一个开发者环境，这个包括但不限于安装IDE (Integrated Development Environment，集成开发环境)。接下来的教程中有关于Eclipse上IDE的操作指南。&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;更多信息，请参见 [[Bukkit/插件开发教程-建立工作区|建立工作区]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
你&#039;&#039;&#039;必须&#039;&#039;&#039;下载&#039;&#039;&#039;Java&#039;&#039;&#039;开发者使用的Eclipse构建版本, &#039;&#039;&#039;不是&#039;&#039;&#039; &#039;&#039;&#039;Java EE&#039;&#039;&#039;开发者的版本。 Java EE 开发者的版本不包含此教程需要的Maven支持。&lt;br /&gt;
&lt;br /&gt;
== 新建插件开发项目  ==&lt;br /&gt;
=== 创建一个新的项目 ===&lt;br /&gt;
&lt;br /&gt;
在开始工作之前，你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击&#039;&#039;File -&amp;gt; New -&amp;gt; Project:&#039;&#039;来创建一个新的项目.&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject.png]] &lt;br /&gt;
&lt;br /&gt;
现在,打开&#039;&#039;Maven&#039;&#039;文件夹, 然后选择&#039;&#039;Maven Project&#039;&#039;.点击next,之后在下一个菜单中选择&#039;&#039;Create a simple project&#039;&#039;, 再次点击&#039;&#039;Next&#039;&#039;:&lt;br /&gt;
如果你看不到&#039;&#039;Maven&#039;&#039;文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject2.png]] &lt;br /&gt;
&lt;br /&gt;
现在，你需要给你的组用户命名，就像下面这样:&lt;br /&gt;
* 如果你拥有一个域名，package则填写逆转的域名地址. &lt;br /&gt;
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]&lt;br /&gt;
** 避免使用一个不属于你自己的域名.&lt;br /&gt;
* 没有域名? 这里有几种方法可供选择。&lt;br /&gt;
*# 在资源管理站点创建一个用户，比如GitHub或是sourceforge&lt;br /&gt;
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 使用你的邮箱.  例如: &amp;lt;username&amp;gt;@gmail.com格式的邮箱应输入com.gmail.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 最不济的方法.：  使用独特的组名命名方式，这应当是你最后的解决方法。&lt;br /&gt;
&lt;br /&gt;
以下几个地址不能作为package中的地址前缀:&lt;br /&gt;
* org.bukkit&lt;br /&gt;
* net.bukkit&lt;br /&gt;
* com.bukkit&lt;br /&gt;
* net.minecraft&lt;br /&gt;
&lt;br /&gt;
完成基础的组名以后，你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 &#039;&#039;TestPlugin&#039;&#039;的插件，那么完整的组名是&#039;&#039;io.github.&amp;lt;username&amp;gt;&#039;&#039;, 你的工程名也是 &#039;&#039;TestPlugin&#039;&#039;. 至于版本，默认即可，稍后可以修改。&lt;br /&gt;
完成向导:&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject3.png]] &lt;br /&gt;
&lt;br /&gt;
如果这是你首次使用Eclipse, 点击右上角的&amp;quot;X&amp;quot; 来关闭Welcome提示页面. 现在，你的窗口视图看起来就像下面的图片这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Eclipsemain.png]] &lt;br /&gt;
&lt;br /&gt;
点击工程名称右边的箭头来进行下一步，现在我们正式开始。&lt;br /&gt;
=== 添加Bukkit API ===&lt;br /&gt;
&lt;br /&gt;
在开发插件之前，你需要添加 Bukkit API库文件到你的项目，作为一个dependency, 你也可以添加其他你想实用的API.&lt;br /&gt;
&lt;br /&gt;
找到项目文件夹中部的&#039;&#039;pom.xml&#039;&#039;并双击进行编辑. 点击&#039;&#039;pom.xml&#039;&#039;中部的 tab,你将会看到下图所示内容:&lt;br /&gt;
&lt;br /&gt;
[[Image:Pomeditor.png]]&lt;br /&gt;
&lt;br /&gt;
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;build&amp;gt;&lt;br /&gt;
       &amp;lt;plugins&amp;gt;&lt;br /&gt;
           &amp;lt;plugin&amp;gt;&lt;br /&gt;
               &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;
               &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
               &amp;lt;configuration&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;amp;lt;&amp;lt;/nowiki&amp;gt;source&amp;gt;1.7&amp;lt;/source&amp;gt;&lt;br /&gt;
                   &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;&lt;br /&gt;
               &amp;lt;/configuration&amp;gt;&lt;br /&gt;
           &amp;lt;/plugin&amp;gt;&lt;br /&gt;
       &amp;lt;/plugins&amp;gt;&lt;br /&gt;
    &amp;lt;/build&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你或许想使用其他版本,例如 &amp;lt;code&amp;gt;1.8&amp;lt;/code&amp;gt; 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码&amp;quot;error&amp;quot;中建议你更改语言版本.同意即可.&lt;br /&gt;
&lt;br /&gt;
在位于代码中段的&#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit&#039;s repository的地址): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;repositories&amp;gt;&lt;br /&gt;
        &amp;lt;repository&amp;gt;&lt;br /&gt;
            &amp;lt;id&amp;gt;bukkit-repo&amp;lt;/id&amp;gt;&lt;br /&gt;
            &amp;lt;url&amp;gt;http://repo.bukkit.org/content/groups/public/&amp;lt;/url&amp;gt;&lt;br /&gt;
        &amp;lt;/repository&amp;gt;&lt;br /&gt;
    &amp;lt;/repositories&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后继续在 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;dependencies&amp;gt;&lt;br /&gt;
        &amp;lt;dependency&amp;gt;&lt;br /&gt;
            &amp;lt;groupId&amp;gt;org.bukkit&amp;lt;/groupId&amp;gt;&lt;br /&gt;
            &amp;lt;artifactId&amp;gt;bukkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
            &amp;lt;version&amp;gt;1.7.2-R0.3&amp;lt;/version&amp;gt;&lt;br /&gt;
            &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;
            &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;
        &amp;lt;/dependency&amp;gt;&lt;br /&gt;
    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.&lt;br /&gt;
&lt;br /&gt;
当你完成上述步骤后,&#039;&#039;pom.xml&#039;&#039;中的内容应该是这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Finishedpom.png]]&lt;br /&gt;
&lt;br /&gt;
通过菜单 &#039;&#039;File -&amp;gt; Save&#039;&#039;或者按住 &amp;lt;code&amp;gt;Ctrl + S&amp;lt;/code&amp;gt; 来保存&#039;&#039;pom.xml&#039;&#039;文件 .  之后, 右键项目标题，然后依次选择 &#039;&#039;Maven -&amp;gt; Update Project&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Bukkit Java文档 ===&lt;br /&gt;
&lt;br /&gt;
 如果你曾经使用过Eclipse和Java，你会知道当你将鼠标准心移至class或者 method中的代码部分时，一个含有相关文档内容的黄色小框会弹出来. 这就是（Java的）Java文档。&lt;br /&gt;
 &lt;br /&gt;
 你可以[http://download.oracle.com/javase/6/docs/api/ Oracle website 在线获取]它.。Bukkit同样拥有文档，这些文档内容包含了关于API中method和class的有用描述。&lt;br /&gt;
 &lt;br /&gt;
 你可以在[http://jd.bukkit.org/ 这]找到它 (Beta版的Java文档在[http://jd.bukkit.org/ 这]找, 新开发出来的Java文档在  &lt;br /&gt;
 &lt;br /&gt;
 [http://jd.bukkit.org/ 这]) 。为了在Eclipse里使用Java文档,这样的话当你的鼠标停留在Bukkit的class和method上时，&lt;br /&gt;
 &lt;br /&gt;
 （Bukkit的）Java文档就会冒出来。  &lt;br /&gt;
JavaDoc in spigot [https://hub.spigotmc.org/javadocs/bukkit/ Click Here]&lt;br /&gt;
&lt;br /&gt;
 首先右键在侧边栏里的&amp;quot;Maven Dependencies&amp;quot;下面的Bukkit jar,选择&amp;quot;Properties&amp;quot;.选中窗口左边的&#039;&#039;Javadoc Location&#039;&#039;, 并粘贴如下网址 &lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;http://jd.bukkit.org/&#039;&#039;&#039; (或者上面的测试版/最新版 Java文档链接也好) 到&amp;quot;Javadoc URL&amp;quot;下面的输入框内. 就像这样: &lt;br /&gt;
&lt;br /&gt;
[[Image:Bukkitjavadocs.png]] &lt;br /&gt;
&lt;br /&gt;
 点击 &amp;quot;Validate&amp;quot;,然后再点击&amp;quot;OK&amp;quot;就完成了.现在Bukkit Javadocs已经连接到Bukkit提供的资源,同时你也可以通过Eclipse来获得帮助类文档信息了.&lt;br /&gt;
&lt;br /&gt;
=== 创建一个包 ===&lt;br /&gt;
&lt;br /&gt;
 现在你需要创建一个包，它将储存所有我们需要使用的Java类文件. 右键展开&#039;&#039;src/main/java&#039;&#039;折叠并选择 &#039;&#039;New &amp;amp;gt; Package&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
[[Image:Newpackage.png]] &lt;br /&gt;
&lt;br /&gt;
 包名的话，先写上你的组名，然后加个点，再写上你的小写Artifact ID。举例, 如果你的组名是&#039;&#039;io.github.name&#039;&#039;，你的artifact名是 &#039;&#039;TestPlugin&#039;&#039;。&lt;br /&gt;
 &lt;br /&gt;
 你的包名就应该是&#039;&#039;io.github.name.testplugin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== 创建插件的类文件 ===&lt;br /&gt;
&lt;br /&gt;
既然我们已经建立好了我们的项目，我们接下来可以开始添加类文件以及制作插件了. 插件的主类指的是拓展(extends)JavaPlugin的类文件. 在你的插件中只能有一个类文件拓展JavaPlugin，无论直接还是间接. 先创建你的主类文件并保持名称与插件名一致是个很好的习惯.右键你之前新建的包,并选择&amp;amp;nbsp; &#039;&#039;New &amp;amp;gt; Class&#039;&#039;.你将会创建一个新的类文件，就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$GroupName}.{$ArtifactName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$ArtifactName} extends JavaPlugin {&lt;br /&gt;
    &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
警告： 插件绝对不应该调用自己的构造函数并实例化&lt;br /&gt;
&lt;br /&gt;
=== 创建plugin.yml ===&lt;br /&gt;
&lt;br /&gt;
你已经创建了项目和主类，如果你想要Bukkit能够加载你的插件，你还必须创建 &#039;&#039;&#039;[[Plugin YAML|plugin.yml]]&#039;&#039;&#039; 文件. 这个文件包含有基础的插件信息，如果缺失这个文件，你的插件也将会失效. 此时你需要右键&#039;&#039;src/main/resources&#039;&#039;. 选择&#039;&#039;New &amp;amp;gt; File&#039;&#039;. 命名为&amp;quot;&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;&amp;quot; 并右键完成新文件的创建.Eclipse会打开默认的文本编辑窗口来显示&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;文件中空白的内容并提供编辑. &#039;&#039;(Hint:&amp;amp;nbsp;如果你想要使得你的工作空间更加规整，关闭文本编辑器并将&#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件拖到主工作空间(拖到文件目录的右边) 之后你就可以在eclipse中编辑文件了.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
有三个基础的内容需要在plugin.yml写明. &lt;br /&gt;
: name:  插件名称. &lt;br /&gt;
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 插件主类的完整，合法名称] . &lt;br /&gt;
: version: 插件当前版本号. &lt;br /&gt;
&lt;br /&gt;
最简单的 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件内就像这样&amp;amp;nbsp;: &lt;br /&gt;
&lt;br /&gt;
 name: {插件名称}&lt;br /&gt;
 main: {包名}.{主类}&lt;br /&gt;
 version: {版本号}&lt;br /&gt;
&lt;br /&gt;
 PS:插件的包名经常会包括插件的名称，所以看到这个的时候不要感到惊讶。&lt;br /&gt;
    你的主类是否是你的插件名取决于你之前的命名方式，时刻记住这一点很重要。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;更多例子&#039;&#039;&#039;, 请看[[#Example_Files_and_Templates]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
此时你的插件已经可以被Bukkit加载了,同时服务端日志也会开始记录你的插件. 不过它现在什么用都没有!&lt;br /&gt;
&lt;br /&gt;
== onEnable()和onDisable()方法  ==&lt;br /&gt;
&lt;br /&gt;
这些方法将在插件启用与卸载时生效. 默认情况下，你的插件在被加载时会调用这些方法，所以你可以在这里注册你需要用到的事件和提供一些调试信息. &amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 方法会在插件启用时被调用, 需要包含初始化插件的逻辑语句. &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法会在插件卸载时被调用，需要包含清理(clean up)插件的逻辑语句和连接声明(associated state)。此外，插件可以重写 &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; 方法来在插件载入时执行附加的逻辑语句。&lt;br /&gt;
&lt;br /&gt;
=== onEnable()和onDisable()方法介绍  ===&lt;br /&gt;
&lt;br /&gt;
在前面的章节中，我们在主类创建了&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法. 此时，这些代码看起来就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
     @Override&lt;br /&gt;
     public void onEnable() {&lt;br /&gt;
         // 插件载入时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     @Override&lt;br /&gt;
     public void onDisable() {&lt;br /&gt;
         // 插件卸载时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这些方法已经创建，但目前它们还没有任何作用.&lt;br /&gt;
注意: 不需要添加代码来实现输出信息&amp;quot;{$PluginName} has been enabled!&amp;quot; ，因为bukkit会自动输出此类信息&lt;br /&gt;
有关更多事件的信息请查看 [[Event_API_Reference|这里]].&lt;br /&gt;
&lt;br /&gt;
=== 发送提示信息 ===&lt;br /&gt;
&lt;br /&gt;
 插件能够输出信息至控制台与服务器日志。这是通过调用插件记录器里正确的方法来实现的。首先我们必须调用 &amp;lt;code&amp;gt;getLogger()&amp;lt;/code&amp;gt; 方法来检索该插件的记录器，&lt;br /&gt;
 &lt;br /&gt;
 然后我们就可以开始记录信息了。&lt;br /&gt;
&lt;br /&gt;
我们通过调用&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法可以实现输出信息。 我们需要在&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法下面插入如下代码：&lt;br /&gt;
&lt;br /&gt;
 getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
在 &amp;lt;code&amp;gt;&#039;&#039;&#039;onDisable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法里面，你也可以做类似的事情，不过要改改输出信息。&lt;br /&gt;
&lt;br /&gt;
此时你的主类文件就像这样: &lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
         @Override&lt;br /&gt;
         public void onEnable() {&lt;br /&gt;
             getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public void onDisable() {&lt;br /&gt;
         getLogger().info(&amp;quot;onDisable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 防止重载后插件失效 ===&lt;br /&gt;
&lt;br /&gt;
你需要知道的是，插件并不只会伴随服务器关闭、启动而重载，你的插件也可能会被其他插件启用、关闭，或是在服务器运行时使用/reload命令. &lt;br /&gt;
假设服务器启动之后一会儿后，你的插件才被启用，这时便是危险的,因为服务器中可能已经有玩家在线、额外的世界被加载以及很多预想不到的不同之处(与&amp;quot;插件同服务器同时加载&amp;quot;的情况相比较). &lt;br /&gt;
&lt;br /&gt;
举个例子来说:&lt;br /&gt;
* 你有一个用来储存HashMap中的玩家登入信息的插件&lt;br /&gt;
* 你希望该信息对所有玩家都可见&lt;br /&gt;
* 一名管理员使用/reload命令&lt;br /&gt;
* 你的插件被卸载，所有储存的数据都丢失了&lt;br /&gt;
* 你的插件被再次启用时，一些玩家已经在线&lt;br /&gt;
* 这些玩家在HashMap并没有储存的信息&lt;br /&gt;
* 你试图重新获得他们的信息，但是失败了!&lt;br /&gt;
&lt;br /&gt;
为了插件重载以后能够正常工作, 你需要在插件启用时加载所有已经在线的玩家的信息并将之储存在HashMap中.&lt;br /&gt;
&lt;br /&gt;
 for (Player player : Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
     playerList.put(player.getName(), playerData(player));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== 监听器  ==&lt;br /&gt;
监听器是一种方法被调用来对事件作出反应的类.所有的监听器使用 org.bukkit.event.Listener接口.更多有关监听器创建的细节,&lt;br /&gt;
:: &#039;&#039; 请查看: [[Event API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 指令  ==&lt;br /&gt;
&lt;br /&gt;
=== onCommand() 方法  ===&lt;br /&gt;
&lt;br /&gt;
 现在你已经知道如何注册一个事件并做出响应,但是如果你只是想要在命令输入之后做出响应呢? 你可以使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 这个代码的作用是当玩家输入“/”时，监听该操作并执行相关语句&amp;quot;/&amp;quot; . 举个例子来说.输入 &amp;quot;/do something&amp;quot; 将会执行 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 此种情况下，因为没有特定的行为被编程，所以并不会发生任何事情。&lt;br /&gt;
&lt;br /&gt;
 请避免使用和bukkit所提供的指令重名的指令, 然后深思你的指令名的唯一性。 &lt;br /&gt;
 &lt;br /&gt;
 例如.指令&amp;quot;give&amp;quot;已经被好几个插件使用了, 如果你执行了另外一个&amp;quot;give&amp;quot;指令, 你的插件将会和这些插件冲突。&lt;br /&gt;
&lt;br /&gt;
 你必须在插件的&amp;quot;plugins.yml&amp;quot;注册你的指令 否则这个方法将不会被触发。&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法必须返回一个布尔值（true或false）。 &lt;br /&gt;
 &lt;br /&gt;
 如果返回值是true，你不会看到什么明显的事情发生。 &lt;br /&gt;
 &lt;br /&gt;
 但如果返回值是false，则会返回你的plugin.yml里的&#039;usage:property&#039;然后发送给命令使用者. &lt;br /&gt;
&lt;br /&gt;
当使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时, 你需要填写4个参数. &lt;br /&gt;
&lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 发送命令的对象&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;Command cmd&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行的指令&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String commandLabel&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行指令的别名&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String[] args&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 该指令的自变量数组。&lt;br /&gt;
 &lt;br /&gt;
例如.指令 &#039;&#039;/hello abc def&#039;&#039; 中， &#039;&#039;abc&#039;&#039; 会被存放进args[0]中, &#039;&#039;def&#039;&#039; 被存放进args[1]中。&lt;br /&gt;
&lt;br /&gt;
==== 设置命令  ====&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { // 如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
     // 所需要执行的事（此处略）&lt;br /&gt;
         return true;&lt;br /&gt;
     } //如果以上内容成功执行，则返回true &lt;br /&gt;
     // 如果执行失败，则返回false.&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 每当使用&amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时， 在每个方法后面都加上一个 return false; 是一个惯例。 &lt;br /&gt;
 &lt;br /&gt;
 返回false会显示在plugin.yml里设置的usage信息 (看下面). 用这种方法的话，一旦执行出错便会显示usage信息。&lt;br /&gt;
&lt;br /&gt;
 每当方法return一个值的时候，这个方法就会退出，所以return true的时候，在它下面的代码就会被跳过, &lt;br /&gt;
 &lt;br /&gt;
 除非return语句在一个if的嵌套中，或者类似的嵌套情况。 &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;.equalsIgnoreCase(&amp;quot;basic&amp;quot;)&#039;&#039;&#039;&amp;lt;/code&amp;gt;代表忽略英文大小写. 在这种情况下,字符串&amp;quot;BAsIc&amp;quot; 和 &amp;quot;BasiC&amp;quot;和 basic相同，代码会被照常执行。&lt;br /&gt;
&lt;br /&gt;
和以前一样，加两行import在你的java文件头&lt;br /&gt;
&lt;br /&gt;
 import org.bukkit.command.Command;&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.command.CommandSender;&lt;br /&gt;
&lt;br /&gt;
=== 在Plugin.yml中添加你的指令  ===&lt;br /&gt;
&lt;br /&gt;
你需要添加你的指令到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件里. 如下是指令/basic添加到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039;的例子（请在plugins.yml的末尾添加如下代码）: &lt;br /&gt;
&lt;br /&gt;
commands:&lt;br /&gt;
   basic:&lt;br /&gt;
      description: This is a demo command.&lt;br /&gt;
      usage: /&amp;lt;command&amp;gt; [player]&lt;br /&gt;
      permission: &amp;lt;plugin name&amp;gt;.basic&lt;br /&gt;
      permission-message: You don&#039;t have &amp;lt;permission&amp;gt;&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;basic&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令名称&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;description&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令描述&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;usage&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 在oncommand方法return false后显示的用法提示。尽量简洁, 使别人能够理解指令是什么以及如何使用它。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被一些显示帮助信息的插件使用，用来决定向玩家显示哪个指令（完全不对劲吧！）。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission-message&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 当玩家使用了这个指令而没有权限时输出的信息。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 PS:yml文件使用两个空格作为制表符,使用tab键输入制表符会导致错误。&lt;br /&gt;
&lt;br /&gt;
===控制台指令vs玩家指令===&lt;br /&gt;
&lt;br /&gt;
 你可能注意到了上文的&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt;参数. &amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt; 是个Bukkit接口，&lt;br /&gt;
 &lt;br /&gt;
 它有两个（对插件编写者）有用的子类：&amp;lt;code&amp;gt;&#039;&#039;&#039;Player&#039;&#039;&#039;&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;&#039;&#039;&#039;ConsoleCommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 所以当你编写插件的时候, 确定这个指令从控制台发出后能完全正常工作是十分必要的,&lt;br /&gt;
 &lt;br /&gt;
 那些只能由在线玩家执行的指令 &#039;&#039;只能&#039;&#039; 由一个在线玩家执行。&lt;br /&gt;
 &lt;br /&gt;
 有的插件在判断命令执 行对象不是玩家时仅仅用return进行处理 (比如当有人从控制台发出指令), &lt;br /&gt;
 &lt;br /&gt;
 即便这些指令能够在控制台完美运行（比如改变天气的指令）。&lt;br /&gt;
&lt;br /&gt;
一个判断sender的方法: &lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { //如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
             // 所需要执行的事（此处略）&lt;br /&gt;
             return true;&lt;br /&gt;
         } else if (cmd.getName().equalsIgnoreCase(&amp;quot;basic2&amp;quot;)) {&lt;br /&gt;
             if (!(sender instanceof Player)) { //如果sender与Player类不匹配&lt;br /&gt;
                 sender.sendMessage(&amp;quot;This command can only be run by a player.&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 Player player = (Player) sender;&lt;br /&gt;
                    // 所需要执行的事（此处略）&lt;br /&gt;
             }&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 在这个例子里，指令&#039;&#039;&#039;basic&#039;&#039;&#039;能由任何人发出 - 一个在线的玩家, 或者服务器后台的op. &lt;br /&gt;
 &lt;br /&gt;
 但是指令&#039;&#039;&#039;basic2&#039;&#039;&#039;只能由一个在线的玩家发出. &lt;br /&gt;
&lt;br /&gt;
 大体上说,你应该允许尽可能多的指令在控制台和玩家聊天框里都正常执行. &lt;br /&gt;
 &lt;br /&gt;
 需要在线玩家执行的指令可以使用以上例子中的机制来检测&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;是一个玩家. &lt;br /&gt;
 &lt;br /&gt;
 很多指令广泛依靠于一些玩家的属性（例如可以被tp或被给予物品等等）。&lt;br /&gt;
&lt;br /&gt;
 如果你想更进一步, 你可以对你的指令的自变量做一些额外的检测， 例如传送指令在提供玩家ID的情况下（也只有在这种情况下）可以在控制台被执行。&lt;br /&gt;
&lt;br /&gt;
=== 使用独立的 CommandExecutor class  ===&lt;br /&gt;
&lt;br /&gt;
 以上的例子必须把 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法放在插件的主类里。&lt;br /&gt;
 &lt;br /&gt;
 对于小插件来说，这是极好的,但如果你要写一个大点的插件,把你的 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法放在独立的类里将会很有意义。&lt;br /&gt;
 &lt;br /&gt;
 幸运的是，这并不难: &lt;br /&gt;
&lt;br /&gt;
*在你的包里创建一个新类. 命名为&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;之类的 (当然，要把&#039;&#039;&#039;MyPlugin&#039;&#039;&#039;替换成你制作的插件的名字)。 这个类 &#039;&#039;&#039;必须&#039;&#039;&#039; 继承Bukkit 的&#039;&#039;&#039;CommandExecutor&#039;&#039;&#039;接口。&lt;br /&gt;
&lt;br /&gt;
*在你的插件的&amp;lt;code&amp;gt;&#039;&#039;&#039;onEnable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法中,你需要实例化你创建的命令执行类(CommandExecutor class), 然后做一个像下面一样的调用：&amp;lt;code&amp;gt;getCommand(&amp;quot;basic&amp;quot;).setExecutor(myExecutor);&amp;lt;/code&amp;gt;&lt;br /&gt;
 其中&amp;quot;basic&amp;quot;是我们需要操控的指令, &amp;lt;code&amp;gt;myExecutor&amp;lt;/code&amp;gt; 是我们创建的实例.&lt;br /&gt;
&lt;br /&gt;
废话少说直接吔我例子: &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyPlugin.java&#039;&#039;&#039; (插件的主类): &lt;br /&gt;
 @Override&lt;br /&gt;
 public void onEnable() {&lt;br /&gt;
         // 如果你没有在plugins.yml注册过指令的话，此处会抛出空指针异常！&lt;br /&gt;
         this.getCommand(&amp;quot;basic&amp;quot;).setExecutor(new MyPluginCommandExecutor(this));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;MyPluginCommandExecutor.java&#039;&#039;&#039;（独立CommandExecutor类）: &lt;br /&gt;
 public class MyPluginCommandExecutor implements CommandExecutor {&lt;br /&gt;
         private final MyPlugin plugin;&lt;br /&gt;
 &lt;br /&gt;
         public MyPluginCommandExecutor(MyPlugin plugin) {&lt;br /&gt;
             this.plugin = plugin; // Store the plugin in situations where you need it.&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
             // 和以前一样执行就好…&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 请注意我们是如何把主类里的对象指向&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;的. 这允许我们轻易的使用主类对象的方法（如果我们需要的话）。&lt;br /&gt;
&lt;br /&gt;
 这么做的话, 我们便能更好的组织代码 - 如果 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法又大又复杂, 它就能被划分为子类，从而不使插件的主类显得凌乱。&lt;br /&gt;
&lt;br /&gt;
 PS：如果你的插件含有多个指令的话, 你需要为每一个指令单独设立一个commandexecutor.&lt;br /&gt;
&lt;br /&gt;
=== 写一个安全的指令 ===&lt;br /&gt;
 当写一个指令的时候,别进行任何假设（很重要）, 例如假设执行者一定是个玩家. 请牢记以下原则:&lt;br /&gt;
&lt;br /&gt;
==== 在命令执行前确定发出者是玩家 ====&lt;br /&gt;
&lt;br /&gt;
使用简单的例子来进行检测:&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (sender instanceof Player) {&lt;br /&gt;
             Player player = (Player) sender;&lt;br /&gt;
             // do something&lt;br /&gt;
         } else {&lt;br /&gt;
             sender.sendMessage(&amp;quot;You must be a player!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
             // do something&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 检测自变量个数 ====&lt;br /&gt;
&lt;br /&gt;
 别老是认为玩家都能打对正确数量的自变量. &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (args.length &amp;gt; 4) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Too many arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         } &lt;br /&gt;
         if (args.length &amp;lt; 2) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Not enough arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 在获取玩家之前先检测他们是否在线 ====&lt;br /&gt;
&lt;br /&gt;
 有时你想通过命令发出者打出的ID来获取其他玩家，请确保他们在线再说！&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         Player target = (Bukkit.getServer().getPlayer(args[0]));&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not online!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 如果你想对离线玩家进行操作，&amp;lt;code&amp;gt;&#039;&#039;&#039;OfflinePlayer&#039;&#039;&#039;&amp;lt;/code&amp;gt;类提供了基础操作的方法.&lt;br /&gt;
&lt;br /&gt;
== 插件的配置/设置  ==&lt;br /&gt;
Bukkit API 为插件提供了一种简便的方法，用来管理用户自定义的设置。同时，通过这种方法也可以储存数据。&lt;br /&gt;
:: &#039;&#039;详情请查看: [[Configuration API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 权限  ==&lt;br /&gt;
&lt;br /&gt;
利用新的Bukkit API来设置权限是再容易不过的事情. 检测玩家是否拥有权限，可以使用以下代码: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;if (player.hasPermission(&amp;quot;some.pointless.permission&amp;quot;)) {&lt;br /&gt;
   //执行此段代码(有权限)&lt;br /&gt;
} else {&lt;br /&gt;
   //执行此段代码(无权限)&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你也可以检测一个权限节点是否被定义过，(等效于 Java 语言中的&#039;&#039;&#039;null&#039;&#039;&#039;)，使用下面的方法: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;boolean isPermissionSet(String name)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你或许对此抱有疑惑:为什么这段代码中没有任何用户群的信息？因为它们并不是必需的. 之前这段代码想要实现的主要功能之一就是根据用户群格式化聊天信息. 但其实使用权限就可以很容易地完成这项工作.你可以在聊天插件的配置文件中定义权限与前缀的对应关系. 举例来说，&amp;quot;someChat.prefix.admin&amp;quot; 权限可以对应前缀[Admin].当拥有此权限的玩家发出聊天信息时 ，聊天前缀便是[Admin]. &lt;br /&gt;
&lt;br /&gt;
另一个想要实现的主要功能则是给所有拥有权限的在线用户发一条信息.你可以这样做: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;for (Player player: Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
    if (player.hasPermission(&amp;quot;send.receive.message&amp;quot;)) {&lt;br /&gt;
        player.sendMessage(&amp;quot;You were sent a message&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
看到这里你可能会问，若是没有权限组我该如何配置玩家的 [[权限]] ？既然 [[Bukkit]] API 本身并没有提供用户组的预设, 你必须通过安装一款权限组插件，像 [[permissionsBukkit]]  这样的插件来帮助你配置用户群. &#039;&#039;&#039;API 只是提供接口而不是提供已实现的类.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
=== 配置权限 ===&lt;br /&gt;
{{Hide|标题=译者注 |内容= 其一，以下代码中部分内容将以&#039;&#039;//文字&#039;&#039;翻译，使用时请不要加入其中内容，仅做教程中的翻译参考；&lt;br /&gt;
其二，default:true在下面翻译为全员默认拥有该权限，default:false则翻译为全员默认没有该权限，请注意理解。 }}&lt;br /&gt;
如果你想要更多地控制你的权限，例如设置权限的默认值或是子权限节点，不如将权限节点加入你的 &#039;&#039;[[plugin.yml]]&#039;&#039;文件.这并不是必须的，但我们推荐你这样做. 下面是一个实例，显示了当你将权限添加在&#039;&#039;plugin.yml&#039;&#039;文件末尾时的样子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands //给予使用doorman所有命令的权限&lt;br /&gt;
        children:  //子权限节点&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&lt;br /&gt;
    doorman.kick:&lt;br /&gt;
        description: Allows you to kick a user  //允许你踢出一名用户&lt;br /&gt;
        default: op  //OP默认拥有该权限&lt;br /&gt;
    doorman.ban:&lt;br /&gt;
        description: Allows you to ban a user  //允许你封禁一名用户&lt;br /&gt;
        default: op //OP默认拥有该权限&lt;br /&gt;
    doorman.knock:&lt;br /&gt;
        description: Knocks on the door! //敲门&lt;br /&gt;
        default: true //全员默认拥有该权限&lt;br /&gt;
    doorman.denied:&lt;br /&gt;
        description: Prevents this user from entering the door  //禁止拥有该权限节点的用户进入门内&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
值得注意的是,你的插件使用的每个权限都被定义为 &#039;&#039;permissions&#039;&#039; 这个节点的子权限节点. 每个权限都能够拥有一个描述，默认值以及子权限节点。&lt;br /&gt;
==== 默认值  ====&lt;br /&gt;
&lt;br /&gt;
当一个权限没有明确是否为玩家所拥有&amp;amp;nbsp;，&#039;&#039;hasPermission&#039;&#039;&amp;lt;ref&amp;gt;这是一个检测玩家是否拥有指定权限节点的方法&amp;lt;/ref&amp;gt;&amp;amp;nbsp;将返回false&amp;lt;ref&amp;gt;该方法返回的boolean值&amp;lt;/ref&amp;gt;. 在你的[[plugin.yml]] you can change this by setting the default node to be one of four values: &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;true&#039;&#039;&#039; - 全员默认拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;false&#039;&#039;&#039; - 全员默认没有该权限. &lt;br /&gt;
*&#039;&#039;&#039;op&#039;&#039;&#039; - 如果玩家是一名OP&amp;lt;ref&amp;gt;Operator，服务器管理员&amp;lt;/ref&amp;gt;，那么他将拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;not op&#039;&#039;&#039; - 如果玩家不是一名OP，那么他将拥有该权限.&lt;br /&gt;
&lt;br /&gt;
==== 子权限  ====&lt;br /&gt;
&lt;br /&gt;
在这之前你可能已经习惯了通过权限节点自动分配所有的子权限&amp;lt;ref&amp;gt;原句:Before now you will probably be used to the * permission to automatically assign all sub permissions.此处的sub permissions应是同上文“你的插件使用的每个权限都被定义为 permissions 这个节点的子权限节点.”相照应&amp;lt;/ref&amp;gt;. 现在这种方法已经随着bukkit API&amp;lt;ref&amp;gt;应用程序编程接口(Application Programming Interface)&amp;lt;/ref&amp;gt;的更新而改变，现在你可以自己定义子权限了. 这将使得插件具有更多的灵活性. 下面是一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands  //翻译见上&lt;br /&gt;
        children:&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
在这里，&#039;&#039;doorman.*&#039;&#039; 权限拥有几个子权限并分配在其下. 子权限工作的方式就是当&#039;&#039;doorman.*&#039;&#039; 权限节点被设置为true时,子权限也会随之设置为 &#039;&#039;plugin.yml&#039;&#039;中的默认值. 然而当&#039;&#039;doorman.*&#039;&#039; 被设置为false时，所有子权限都会设置为与默认值相反的值（true-&amp;gt;false,false-&amp;gt;true）.&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设置你自己的权限  ===&lt;br /&gt;
&lt;br /&gt;
如果你希望想知道更多有关开发你自己的权限插件的事情(即是设置权限的管理类插件) ，那么请前往查看教程&amp;amp;nbsp;[[Developing a permissions plugin]].&lt;br /&gt;
&lt;br /&gt;
== Scheduling Tasks and Background Tasks  ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 方块操控&amp;lt;br&amp;gt;  ==&lt;br /&gt;
&lt;br /&gt;
这是一个很容易的方法创建、获取或修改一个方块, 例如当你想要修改往你头上数第五格的方块时, 首先你需要做的是获取你当前的坐标点, 然后往你当前的坐标点的Y轴加上五, 获取目标方块后我们便能够很容易地修改它了, 例子如下:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@EventHandler&lt;br /&gt;
public void onPlayerMove(PlayerMoveEvent event) {&lt;br /&gt;
    // 得到玩家当前的坐标.&lt;br /&gt;
    Location loc = event.getPlayer().getLocation();&lt;br /&gt;
    // 设置目标坐标为玩家头顶 5 格处.&lt;br /&gt;
    loc.setY(loc.getY() + 5);&lt;br /&gt;
    // 得到目标坐标的方块&lt;br /&gt;
    Block b = loc.getBlock();&lt;br /&gt;
    // 将目标坐标的方块更改为石头 (stone).&lt;br /&gt;
    b.setType(Material.STONE);&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
上面的代码获取了玩家的所在位置，获取头上的第五个方块，并且放上一个石头（stone）。 Note that once you have a &amp;lt;code&amp;gt;Block&amp;lt;/code&amp;gt;, there are other things you can do besides set its type. 你可以查阅JavaDoc获取更多的东西。&lt;br /&gt;
&lt;br /&gt;
你可以用这样相似的观念去生成一些建筑，并且还可以用编程的方式通过算法生成一些独特的方块（这里指由很多个方块拼在一起的四四方方的方块）。比如，去生成一个实心的方块建筑，你可能在生成建筑的时候还需要用到&amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;循环，去一遍遍循环来生成这些建筑，填满这些建筑等。&lt;br /&gt;
下面，是一个在指定坐标生成一个实心的方块建筑的实例方法：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void generateCube(Location loc, int length) {&lt;br /&gt;
    // 放置这个方块最中间的方块并且获取这个方块的坐标。&lt;br /&gt;
    // Uses getBlockN() instead of getN() to avoid casting to an int later.&lt;br /&gt;
    int x1 = loc.getBlockX(); &lt;br /&gt;
    int y1 = loc.getBlockY();&lt;br /&gt;
    int z1 = loc.getBlockZ();&lt;br /&gt;
&lt;br /&gt;
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.&lt;br /&gt;
    int x2 = x1 + length;&lt;br /&gt;
    int y2 = y1 + length;&lt;br /&gt;
    int z2 = z1 + length;&lt;br /&gt;
&lt;br /&gt;
    World world = loc.getWorld();&lt;br /&gt;
&lt;br /&gt;
    // Loop over the cube in the x dimension.&lt;br /&gt;
    for (int xPoint = x1; xPoint &amp;lt;= x2; xPoint++) { &lt;br /&gt;
        // Loop over the cube in the y dimension.&lt;br /&gt;
        for (int yPoint = y1; yPoint &amp;lt;= y2; yPoint++) {&lt;br /&gt;
            // Loop over the cube in the z dimension.&lt;br /&gt;
            for (int zPoint = z1; zPoint &amp;lt;= z2; zPoint++) {&lt;br /&gt;
                // Get the block that we are currently looping over.&lt;br /&gt;
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);&lt;br /&gt;
                // 设置方块ID为57号(钻石块!)&lt;br /&gt;
                currentBlock.setType(Material.DIAMOND_BLOCK); //这里下面会提到&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
这个方法将会在程序给予这个方法高度和起始坐标（最中间的方块的坐标）时，构建一个3D的方块建筑。简单的删除方块也可以使用这个方法的思路实现，但是，但是设置方块的时候，记得把上面标注会提到的那行，设置为物品ID为0的方块(空气方块)。&amp;lt;br&amp;gt; &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== (玩家)修改背包  ==&lt;br /&gt;
&lt;br /&gt;
这个部分大部分讲的是修改玩家的背包。但是在某些情况下，修改箱子里的内容与此相同，当然得在你能找到获取箱子里内容的方法才可以。&amp;amp;nbsp;:P. 这是一个修改玩家背包的一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void onPlayerJoin(PlayerJoinEvent evt) {&lt;br /&gt;
    Player player = evt.getPlayer(); // 当玩家加入游戏&lt;br /&gt;
    PlayerInventory inventory = player.getInventory(); // 获取玩家背包列表&lt;br /&gt;
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // 生成一组钻石&lt;br /&gt;
        &lt;br /&gt;
    if (inventory.contains(itemstack)) {&lt;br /&gt;
        inventory.addItem(itemstack); // 将一组钻石放到玩家的背包里&lt;br /&gt;
        player.sendMessage(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;); //向玩家发送消息(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
所以，上面代码里面的 onPlayerJoin 方法中，我们首先构建了一些变量，让我们的工作变得更容易一些。变量分别是： player, inventory and itemstack。 inventory 变量是玩家的背包内容，itemstack是那64个钻石的物品栈。然后我们判断了玩家是不是有一组钻石。如果有，我们就用inventory.addItem(itemstack)方法再给玩家一组钻石，并且给他发送一个消息“Wow！你看上去很土豪啊！”。 所以修改背包并不像我们想象的那么难，如果我们想实现让玩家进入游戏，检测到一组钻石后删掉这组钻石，只需要把代码里的inventory.addItem(itemstack)替换成inventory.remove(itemstack)。或许你还需要把“Wow！你看上去很土豪啊！”改成“Wow！你看上去好穷啊！”2333333。&lt;br /&gt;
&lt;br /&gt;
== 控制物品 ==&lt;br /&gt;
&lt;br /&gt;
当你需要写一个对物品们做出行为的代码时，你要使用ItemStack类来查阅有关那个栈的所有设置信息。&lt;br /&gt;
&lt;br /&gt;
=== 附魔属性 ===&lt;br /&gt;
&lt;br /&gt;
你想给一个物品附魔，你首先需要了解[http://www.minecraftwiki.net/wiki/Data_values Item Code]和[http://www.minecraftwiki.net/wiki/Talk:Enchanting#Effect_IDs Effect ID].  Enchantments他们自己不能例示(new Enchantment() won&#039;t work) 因为他们是抽象的。所以你必须要用一个EnchantmentWrapper。如果你想去附魔物品，还不想在默认的SMP里附魔，可以使用addEnchantment()里面的addUnsafeEnchantment()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
int itemCode = 280;  //use the item code you want here&lt;br /&gt;
int effectId = 20;  //use the enchantment code you want here&lt;br /&gt;
int enchantmentLevel = 100;&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(itemCode);  //new item of item code&lt;br /&gt;
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id&lt;br /&gt;
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //给物品附魔&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ItemMeta ===&lt;br /&gt;
You can set the display name of an item by doing this.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
String myDisplayName = &amp;quot;Awesome Sword&amp;quot;; //这里是保存了你想要显示的名字&lt;br /&gt;
 &lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你要更改的方块&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //得到方块的元数据&lt;br /&gt;
im.setDisplayName(myDisplayName); //设置它显示的名字&lt;br /&gt;
myItem.setItemMeta(im); //保存更改&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
You can also set the lores of an item. The lores are the small annotations on an item, like &amp;quot;+5 attack damage&amp;quot; on a stone sword.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;String&amp;gt; lores = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
lores.add(&amp;quot;Example lore&amp;quot;, &amp;quot;this one comes on line 2&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again&lt;br /&gt;
im.setLore(lores); //add the lores of course&lt;br /&gt;
myItem.setItemMeta(im); //give the item the new itemmeta&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maps, and Sets, and Lists, Oh My! &amp;lt;ref&amp;gt;标题中Map,Set,List均为Java类集中的接口，故不翻译&amp;lt;/ref&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
除了Map类和HashMap类，Java中还提供了其它的数据结构类集。 &lt;br /&gt;
&lt;br /&gt;
他们提供这些不同的类，是因为有些时候，使用Map类并不合适。&amp;lt;nowiki&amp;gt;[[Java data structure classes]]&amp;lt;/nowiki&amp;gt;在这个条目中，有对Java数据框架类集更深入的讲述。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;=== HashMaps概览以及如何使用此接口  ===&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;Keep in mind to never use a player in a hashmap!&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;&amp;lt;ref&amp;gt;下面有讲解。&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
应在HashMap中使用字符串.，使用 &amp;quot;p.getName()&amp;quot; 来添加，移除或是检查列表中是否含有某个玩家.&lt;br /&gt;
&lt;br /&gt;
将玩家当作object储存在HashMap中会造成大量内存泄漏.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;adam&amp;quot; as the key and assigned a value of &amp;quot;a&amp;quot; to it. That would work as intended, but then say afterwards I wanted to assign the value of &amp;quot;b&amp;quot; to key &amp;quot;adam&amp;quot; I would be able to and would get no errors but the value of &amp;quot;a&amp;quot; assigned to key &amp;quot;adam&amp;quot; in the HashMap would be overwritten because HashMaps cannot contain duplicate values. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Defining a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public Map&amp;lt;KeyType, DataType&amp;gt; HashMapName = new HashMap&amp;lt;&amp;gt;(); //Example syntax&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Example Declaration&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; pluginEnabled = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; isGodMode = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So inside on command you&#039;ll need this, the function name can be different but for the sake of simplicity it&#039;s best if you keep it the same. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;Player player = (Player) sender;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
togglePluginState(player);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void togglePluginState(Player player) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Notice how we use the player name as the key here,&lt;br /&gt;
&lt;br /&gt;
    // not the player object&lt;br /&gt;
&lt;br /&gt;
    String playerName = player.getName();&lt;br /&gt;
&lt;br /&gt;
    if (pluginEnabled.containsKey(playerName)) {&lt;br /&gt;
&lt;br /&gt;
        if (pluginEnabled.get(playerName)) {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, false);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin disabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, true);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    } else {&lt;br /&gt;
&lt;br /&gt;
        pluginEnabled.put(playerName, true); //If you want plugin disabled by default change this value to false.&lt;br /&gt;
&lt;br /&gt;
        player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== More Ideas for HashMaps  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A HashMap (or really any kind of Map in Java) is an association. It allows quick and efficient lookup of some sort of &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;value&#039;&#039;&#039;, given a unique &#039;&#039;&#039;key&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;. Anywhere this happens in your code, a Map may be your solution. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here are a few other ideas which are ideally suited to using Maps. As you will see, it doesn&#039;t have to be data that you store per player, but can be any kind of data that needs to be &amp;quot;translated&amp;quot; from one form to another. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Data Value Lookups  =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Integer&amp;gt; wool_colors = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;orange&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;magenta&amp;quot;, 2);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;light blue&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
   ..&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;black&amp;quot;, 15);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this in response to user commands - turn &amp;quot;green&amp;quot; into 13&lt;br /&gt;
&lt;br /&gt;
int datavalue = 0;&lt;br /&gt;
&lt;br /&gt;
if (wool_colors.containsKey(argument)) {&lt;br /&gt;
&lt;br /&gt;
    datavalue = wool_colors.get(argument);&lt;br /&gt;
&lt;br /&gt;
} else {&lt;br /&gt;
&lt;br /&gt;
    try { datavalue = Integer.parseInt(argument); }&lt;br /&gt;
&lt;br /&gt;
    catch (Exception e) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Saving/Loading a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you don&#039;t want an administrator to edit the data manually&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you need to save data in binary format (too complex to organize for YAML)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you want to avoid parsing block names and/or other objects from freeform text&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is very simple way how to save any HashMap. You can replace HashMap&amp;amp;amp;lt;String, Integer&amp;amp;amp;gt; with any type of HashMap you want. Let&#039;s take an example HashMap with code to save it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        HashMap&amp;lt;String, Integer&amp;gt; mapToSave = new HashMap&amp;lt;String,Integer&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
        public void save(HashMap&amp;lt;String, Integer&amp;gt; map, String path) {&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(map);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
//Handle I/O exceptions&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
save(mapToSave, getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see it&#039;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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public HashMap&amp;lt;String, Integer&amp;gt; load(String path) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
Object result = ois.readObject();&lt;br /&gt;
&lt;br /&gt;
//you can feel free to cast result to HashMap&amp;lt;String, Integer&amp;gt; if you know there&#039;s that HashMap in the file&lt;br /&gt;
&lt;br /&gt;
return (HashMap&amp;lt;String, Integer&amp;gt;)result;&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HashMap&amp;lt;Integer, String&amp;gt; loadedMap;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
String path = getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
File file = new File(path);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if (file.exists()) { // check if file exists before loading to avoid errors!&lt;br /&gt;
&lt;br /&gt;
loadedMap  = load(path);&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
You can use this &amp;quot;API&amp;quot; for saving/loading HashMaps, ArrayLists, and all Objects which implement Serializable or Externalizable interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;/** SLAPI = Saving/Loading API&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 * API for Saving and Loading Objects.&lt;br /&gt;
&lt;br /&gt;
 * Everyone has permission to include this code in their plugins as they wish :)&lt;br /&gt;
&lt;br /&gt;
 * @author Tomsik68&amp;lt;tomsik68@gmail.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
public class SLAPI&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; void save(T obj,String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(obj);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; T load(String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
T result = (T)ois.readObject();&lt;br /&gt;
&lt;br /&gt;
ois.close();&lt;br /&gt;
&lt;br /&gt;
return result;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Example implementation of this API: &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;I&#039;m skipping some part of code in this source&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public class Example extends JavaPlugin {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
private ArrayList&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onEnable() {&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
list = SLAPI.load(&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                //handle the exception&lt;br /&gt;
&lt;br /&gt;
                e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onDisable()	{&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
SLAPI.save(list,&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                 e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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&#039;re writing your own data object classes, and you may want to save their state using this technique, you should read about Java&#039;s Serializable or Externalizable interface. The only difference between Externalizable and Serializable is, that Serializable automatically takes all of class&#039;s fields and tries to serialize them, while Externalizable allows you to define method for reading and writing the Object. It&#039;s easy to add to your code, and it will make your data persistent with very little work required. No more parsing!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note #2: This API doesn&#039;t support changes. Once you change something in the class, data files saved with older version of your plugin won&#039;t load correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Tips &amp;amp; Examples =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.) Simplify your save structure&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.) Save version number along with data&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always remember, that you don&#039;t know what you&#039;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&#039;t know!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.) Migrate older files&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your plugin finds older version of some file, it should update the file accordingly and change version number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 元数据  ==&lt;br /&gt;
&lt;br /&gt;
Bukkit正在尝试尽可能的让制作插件变得更简单，所以带有一个键值例如Player, Entity, World，甚至是一个block的HashMaps都可以用元数据取缔掉。元数据是一些替代HashMap的方式。他可以允许你去自定义&amp;quot;fields&amp;quot;给Players, Entities, Worlds and Blocks.这些东西都是元数据类的一个成员(详见[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&#039;re creating an economy plugin, you would need a HashMap of Player and Float or Double. 如果用了元数据，你不必去做! You just attach to player new metadata 值, and that&#039;s it!&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== 为何要去使用元数据  ===&lt;br /&gt;
&lt;br /&gt;
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps. &lt;br /&gt;
* 元数据可以用来跟其他插件分享信息与数据。&lt;br /&gt;
&lt;br /&gt;
=== 为什么不去使用元数据&amp;lt;br&amp;gt;  ===&lt;br /&gt;
&lt;br /&gt;
* Slightly more difficult to get the value.&lt;br /&gt;
* It is not saved on shutdown (but then again, neither are any Maps that you create).&lt;br /&gt;
&lt;br /&gt;
=== 获取 &amp;amp;amp; 设置元数据  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {&lt;br /&gt;
  object.setMetadata(key, new FixedMetadataValue(plugin,value));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Object getMetadata(Metadatable object, String key, Plugin plugin) {&lt;br /&gt;
  List&amp;lt;MetadataValue&amp;gt; values = object.getMetadata(key);  &lt;br /&gt;
  for (MetadataValue value : values) {&lt;br /&gt;
     // Plugins are singleton objects, so using == is safe here&lt;br /&gt;
     if (value.getOwningPlugin() == plugin) {&lt;br /&gt;
        return value.value();&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
  return null;&lt;br /&gt;
}&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note: If you&#039;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.&lt;br /&gt;
&lt;br /&gt;
== Databases  ==&lt;br /&gt;
&lt;br /&gt;
Sometimes flat files aren&#039;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).&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there&#039;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!) &lt;br /&gt;
&lt;br /&gt;
=== SQLite  ===&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;s very straightforward and shouldn&#039;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]. &lt;br /&gt;
&lt;br /&gt;
SQLite is great for very simple databases, because there&#039;s no server concerns to set up. Just make a few calls to create a new database and table. It&#039;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&#039;s often easiest and fastest to get the SQL basics squared away with SQLite, even if you &amp;quot;graduate&amp;quot; to a more server-class database engine later.&lt;br /&gt;
&lt;br /&gt;
=== MySQL  ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 部署你的插件  ==&lt;br /&gt;
&lt;br /&gt;
 总之你写好的你的插件,如何把它变成一个可安装在服务端的jar文件呢?&lt;br /&gt;
 &lt;br /&gt;
 首先我们需要建立一个水桶服务器。 [http://wiki.bukkit.org/Setting_up_a_server 在这里] 能找到开服相关信息.接下来你需要把插件导出为一个.jar文件，&lt;br /&gt;
 &lt;br /&gt;
 这样你就能在你的服务器里运行它。 在Eclipse里, 右键工程，点击&#039;&#039;Run as &amp;gt; Maven install&#039;&#039;即可:&lt;br /&gt;
&lt;br /&gt;
[[Image:Maveninstall.png]]&lt;br /&gt;
&lt;br /&gt;
 将来呢, 当你对插件进行了改动后,在做上述操作之前，如果你想删除之前的.jar文件，你可以右键工程，点击 &#039;&#039;Run as &amp;gt; Maven clean&#039;&#039; 。 &lt;br /&gt;
 &lt;br /&gt;
 如果你在编译插件时遇到了问题,  请检查你的JDK是否正确安装， 并浏览[[建立工作区]]条目。&lt;br /&gt;
 &lt;br /&gt;
 如果你在Eclipse控制台看到了和JDK有关的错误，你可能需要手动调整JDK,因为Eclipse的检测可能发生了错误。 &lt;br /&gt;
 &lt;br /&gt;
 点击&#039;&#039;Window -&amp;gt; Preferences&#039;&#039;, 然后是&#039;&#039;Java -&amp;gt; Installed JREs&#039;&#039;。选中你最近安装的JDK作为Java运行环境（JRE）并添加, 并取消掉原来那个报错的JDK:&lt;br /&gt;
&lt;br /&gt;
[[Image:Jrelocation.png]]&lt;br /&gt;
&lt;br /&gt;
 如果你的工程成功被编译,JAR文件会生产在 &#039;&#039;target&#039;&#039; 目录下，具体在Eclipse的工作空间里名字为工程名的文件夹中。&lt;br /&gt;
 &lt;br /&gt;
 这个JAR文件就是一个能正常工作的Bukkit插件了。当然前提是你的plugin.yml里没出差错。&lt;br /&gt;
 &lt;br /&gt;
 你可以把这个jar文件丢到服务器的plugins文件夹里,重载或重启服务器,就可以测试你的新插件啦。&lt;br /&gt;
 &lt;br /&gt;
 为了连接上使用你自己的电脑开的服务器 ,在多人游戏的ip里填上你的本地ip即可。&lt;br /&gt;
 &lt;br /&gt;
 如果出现了错误而你又不能解决的话, 试试这里[http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], &lt;br /&gt;
 &lt;br /&gt;
 在这里提问[http://wiki.bukkit.org/IRC bukkitdev IRC channel],或者重新阅读wiki。&lt;br /&gt;
 &lt;br /&gt;
 当你做完了一个实用的插件,可以考虑把它发表在这里[http://dev.bukkit.org/ dev.bukkit]（为了Bukkit社区的访问量）。&lt;br /&gt;
 &lt;br /&gt;
 以上可知,JAR格式是默认压缩生成的(JAR文件基于ZIP文件的格式)。这么说来，在上传插件到Bukkit上时把JAR文件压缩成ZIP只会增加文件体积罢了。&lt;br /&gt;
 &lt;br /&gt;
 而且呢, 放在JAR中的config文件可以在检测不到插件目录（插件自动生成的文件夹，名字与插件名相同，里面是该插件的配置）下的config文件时自动生成。&lt;br /&gt;
 &lt;br /&gt;
 所以我们一般不把jar文件转换成其他格式。&lt;br /&gt;
&lt;br /&gt;
== 导入其它插件 ==&lt;br /&gt;
你可能会希望编辑其它那些有源代码的插件. 如果插件在它的目录下有&#039;&#039;pom.xml&#039;&#039;(大多数知名插件,例如WorldEdit和Essentials都有),你可以通过点击&#039;&#039;File -&amp;gt; Import&#039;&#039;来把它作为一个工程导入, 然后打开&#039;&#039;Maven&#039;&#039;项并选择&#039;&#039;Existing Maven Projects&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
[[Image:Importmaven.png]]&lt;br /&gt;
&lt;br /&gt;
然后选择&#039;&#039;pom.xml&#039;&#039;所在的目录,这个工程就应该出现在你的侧边栏里了。就像平常一样编辑并编译它吧。&lt;br /&gt;
&lt;br /&gt;
== 小贴士  ==&lt;br /&gt;
&lt;br /&gt;
=== 使玩家着火 ===&lt;br /&gt;
&lt;br /&gt;
 Bukkit API有很多碉堡的功能。以下是实现一些功能的代码片段。 &lt;br /&gt;
&lt;br /&gt;
下面的代码运行一个玩家使另一个玩家着火。通过类似于&#039;&#039;&#039;/ignite Notch&#039;&#039;&#039;这样的指令，这个指令会让 Notch 着火。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     // 使用 equalsIgnoreCase() 来忽视大小写&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;ignite&amp;quot;)) {&lt;br /&gt;
         // 确保玩家只输入了一个自变量(要被点着的玩家名).&lt;br /&gt;
         if (args.length != 1) {&lt;br /&gt;
             // 当return false时，该指令的使用信息会显示出来。&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 确保命令发出者是个玩家.&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can set other players on fire.&amp;quot;);&lt;br /&gt;
             sender.sendMessage(&amp;quot;This is an arbitrary requirement for demonstration purposes only.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 获取那个要被点着的玩家. 请注意下标起始是0，不是1.&lt;br /&gt;
         Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
 &lt;br /&gt;
         // 确保这个玩家在线。&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 让这个玩家着火1000 ticks (一秒钟大概有20ticks,所以总共是50秒).&lt;br /&gt;
         target.setFireTicks(1000);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 干掉玩家  ===&lt;br /&gt;
&lt;br /&gt;
 和标题一样.这里是要干掉某个玩家。 &lt;br /&gt;
&lt;br /&gt;
在你的onCommand方法里使用这个: &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;KillPlayer&amp;quot;)) {&lt;br /&gt;
         Player target = sender.getServer().getPlayer(args[0]);&lt;br /&gt;
          // 确保玩家在线.&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         target.setHealth(0); &lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这里有个小扩展,会使用一个爆炸来杀死玩家: &lt;br /&gt;
&lt;br /&gt;
 float explosionPower = 4F; //爆炸等级 - TNT的爆炸等级默认是4F&lt;br /&gt;
 Player target = sender.getWorld().getPlayer(args[0]);&lt;br /&gt;
 target.getWorld().createExplosion(target.getLocation(), explosionPower);&lt;br /&gt;
 target.setHealth(0);&lt;br /&gt;
&lt;br /&gt;
=== 创造一个假爆炸  ===&lt;br /&gt;
&lt;br /&gt;
这段代码会生产一个看起来和听起来与TNT/苦力怕产生的爆炸一样的效果。但是不会对实体和方块产生破坏。 在保留爆炸的艺术的同时又削弱了爆炸的威力，真是太有用了（&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onExplosionPrime(ExplosionPrimeEvent event) {	&lt;br /&gt;
     Entity entity = event.getEntity();&lt;br /&gt;
 &lt;br /&gt;
     // 如果事件和点燃的TNT有关,执行一些代码（又略）&lt;br /&gt;
     if (entity instanceof TNTPrimed) {&lt;br /&gt;
         entity.getWorld().createExplosion(entity.getLocation(), 0);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 使一个玩家对另外一个玩家隐形  ===&lt;br /&gt;
&lt;br /&gt;
 通过输入指令，能让指令执行者对特定的玩家隐形。 然而其他人仍然能看到指令执行者。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;HideMe&amp;quot;) &amp;amp;&amp;amp; args.length == 1) {&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can use this command!&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         // 检测指令执行者是玩家后，我们便可以安全的进行强制转换类操作。&lt;br /&gt;
        Player s = (Player) sender;&lt;br /&gt;
 &lt;br /&gt;
        // 获取命令执行者指定的玩家&lt;br /&gt;
        Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
        if (target == null) {&lt;br /&gt;
            sender.sendMessage(&amp;quot;Player &amp;quot; + args[0] + &amp;quot; is not online.&amp;quot;);&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        // 将 s 从 target 的视野中隐藏起来.&lt;br /&gt;
        target.hidePlayer(s);&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 在玩家准星所指处生产闪电 ===&lt;br /&gt;
&lt;br /&gt;
如下代码运行手持鱼竿的玩家通过点击来生成闪电 (当然要用准星瞄准)。 真是个简单好玩的恶作剧（。&lt;br /&gt;
&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onPlayerInteractBlock(PlayerInteractEvent event) {&lt;br /&gt;
     Player player = event.getPlayer();&lt;br /&gt;
     if (player.getItemInHand().getType() == Material.FISHING_ROD) {&lt;br /&gt;
         // 在给定坐标中生成一道闪电. 在本例中, 这个坐标是玩家准星瞄准的地方.&lt;br /&gt;
         // 只能指向200格以内的坐标.&lt;br /&gt;
         player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 自动格式 ===&lt;br /&gt;
&lt;br /&gt;
 Eclipse提供了将你的代码自动转成Oracle规定的格式, 修正了不符合常规的缩进, 空格等等。 在侧边栏选中你的项目,然后选择&#039;&#039;Source -&amp;gt; Format&#039;&#039;即可对其进行更改。&lt;br /&gt;
&lt;br /&gt;
== 插件需求专区  ==&lt;br /&gt;
&lt;br /&gt;
http://forums.bukkit.org/forums/plugin-requests.13/&lt;br /&gt;
&lt;br /&gt;
== 范例文件和样板  ==&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/wpeTPx7N Example.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have any more questions on this matter, don&#039;t hesitate to visit the BukkitDev [[IRC]] channel and ask!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1412</id>
		<title>Bukkit/插件开发教程</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1412"/>
		<updated>2015-09-03T09:01:38Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​更正错别字&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
{{模板:待完善}}&lt;br /&gt;
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]&lt;br /&gt;
== 介绍  ==&lt;br /&gt;
&lt;br /&gt;
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.&lt;br /&gt;
&lt;br /&gt;
== 学习Java  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;理解这些教程需要具备Java这门编程语言的基本知识.&#039;&#039;&#039;如果你刚开始学习Java或者需要重温一下相关知识，下面有一个并不完整的网站列表可供参考.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;[http://docs.oracle.com/javase/tutorial/ Oracle&#039;s Java Tutorials]&amp;lt;/big&amp;gt; - 官方教程&lt;br /&gt;
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程&lt;br /&gt;
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程&lt;br /&gt;
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书&lt;br /&gt;
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.&lt;br /&gt;
&lt;br /&gt;
== 开发环境  ==&lt;br /&gt;
&lt;br /&gt;
在编写一个插件前 (或学习Java前) 你需要配置一个开发者环境，这个包括但不限于安装IDE (Integrated Development Environment，集成开发环境)。接下来的教程中有关于Eclipse上IDE的操作指南。&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;更多信息，请参见 [[Bukkit/插件开发教程-建立工作区|建立工作区]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
你&#039;&#039;&#039;必须&#039;&#039;&#039;下载&#039;&#039;&#039;Java&#039;&#039;&#039;开发者使用的Eclipse构建版本, &#039;&#039;&#039;不是&#039;&#039;&#039; &#039;&#039;&#039;Java EE&#039;&#039;&#039;开发者的版本。 Java EE 开发者的版本不包含此教程需要的Maven支持。&lt;br /&gt;
&lt;br /&gt;
== 新建插件开发项目  ==&lt;br /&gt;
=== 创建一个新的项目 ===&lt;br /&gt;
&lt;br /&gt;
在开始工作之前，你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击&#039;&#039;File -&amp;gt; New -&amp;gt; Project:&#039;&#039;来创建一个新的项目.&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject.png]] &lt;br /&gt;
&lt;br /&gt;
现在,打开&#039;&#039;Maven&#039;&#039;文件夹, 然后选择&#039;&#039;Maven Project&#039;&#039;.点击next,之后在下一个菜单中选择&#039;&#039;Create a simple project&#039;&#039;, 再次点击&#039;&#039;Next&#039;&#039;:&lt;br /&gt;
如果你看不到&#039;&#039;Maven&#039;&#039;文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject2.png]] &lt;br /&gt;
&lt;br /&gt;
现在，你需要给你的组用户命名，就像下面这样:&lt;br /&gt;
* 如果你拥有一个域名，package则填写逆转的域名地址. &lt;br /&gt;
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]&lt;br /&gt;
** 避免使用一个不属于你自己的域名.&lt;br /&gt;
* 没有域名? 这里有几种方法可供选择。&lt;br /&gt;
*# 在资源管理站点创建一个用户，比如GitHub或是sourceforge&lt;br /&gt;
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 使用你的邮箱.  例如: &amp;lt;username&amp;gt;@gmail.com格式的邮箱应输入com.gmail.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 最不济的方法.：  使用独特的组名命名方式，这应当是你最后的解决方法。&lt;br /&gt;
&lt;br /&gt;
以下几个地址不能作为package中的地址前缀:&lt;br /&gt;
* org.bukkit&lt;br /&gt;
* net.bukkit&lt;br /&gt;
* com.bukkit&lt;br /&gt;
* net.minecraft&lt;br /&gt;
&lt;br /&gt;
完成基础的组名以后，你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 &#039;&#039;TestPlugin&#039;&#039;的插件，那么完整的组名是&#039;&#039;io.github.&amp;lt;username&amp;gt;&#039;&#039;, 你的工程名也是 &#039;&#039;TestPlugin&#039;&#039;. 至于版本，默认即可，稍后可以修改。&lt;br /&gt;
完成向导:&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject3.png]] &lt;br /&gt;
&lt;br /&gt;
如果这是你首次使用Eclipse, 点击右上角的&amp;quot;X&amp;quot; 来关闭Welcome提示页面. 现在，你的窗口视图看起来就像下面的图片这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Eclipsemain.png]] &lt;br /&gt;
&lt;br /&gt;
点击工程名称右边的箭头来进行下一步，现在我们正式开始。&lt;br /&gt;
=== 添加Bukkit API ===&lt;br /&gt;
&lt;br /&gt;
在开发插件之前，你需要添加 Bukkit API库文件到你的项目，作为一个dependency, 你也可以添加其他你想实用的API.&lt;br /&gt;
&lt;br /&gt;
找到项目文件夹中部的&#039;&#039;pom.xml&#039;&#039;并双击进行编辑. 点击&#039;&#039;pom.xml&#039;&#039;中部的 tab,你将会看到下图所示内容:&lt;br /&gt;
&lt;br /&gt;
[[Image:Pomeditor.png]]&lt;br /&gt;
&lt;br /&gt;
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;build&amp;gt;&lt;br /&gt;
       &amp;lt;plugins&amp;gt;&lt;br /&gt;
           &amp;lt;plugin&amp;gt;&lt;br /&gt;
               &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;
               &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
               &amp;lt;configuration&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;amp;lt;&amp;lt;/nowiki&amp;gt;source&amp;gt;1.7&amp;lt;/source&amp;gt;&lt;br /&gt;
                   &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;&lt;br /&gt;
               &amp;lt;/configuration&amp;gt;&lt;br /&gt;
           &amp;lt;/plugin&amp;gt;&lt;br /&gt;
       &amp;lt;/plugins&amp;gt;&lt;br /&gt;
    &amp;lt;/build&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你或许想使用其他版本,例如 &amp;lt;code&amp;gt;1.8&amp;lt;/code&amp;gt; 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码&amp;quot;error&amp;quot;中建议你更改语言版本.同意即可.&lt;br /&gt;
&lt;br /&gt;
在位于代码中段的&#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit&#039;s repository的地址): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;repositories&amp;gt;&lt;br /&gt;
        &amp;lt;repository&amp;gt;&lt;br /&gt;
            &amp;lt;id&amp;gt;bukkit-repo&amp;lt;/id&amp;gt;&lt;br /&gt;
            &amp;lt;url&amp;gt;http://repo.bukkit.org/content/groups/public/&amp;lt;/url&amp;gt;&lt;br /&gt;
        &amp;lt;/repository&amp;gt;&lt;br /&gt;
    &amp;lt;/repositories&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后继续在 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;dependencies&amp;gt;&lt;br /&gt;
        &amp;lt;dependency&amp;gt;&lt;br /&gt;
            &amp;lt;groupId&amp;gt;org.bukkit&amp;lt;/groupId&amp;gt;&lt;br /&gt;
            &amp;lt;artifactId&amp;gt;bukkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
            &amp;lt;version&amp;gt;1.7.2-R0.3&amp;lt;/version&amp;gt;&lt;br /&gt;
            &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;
            &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;
        &amp;lt;/dependency&amp;gt;&lt;br /&gt;
    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.&lt;br /&gt;
&lt;br /&gt;
当你完成上述步骤后,&#039;&#039;pom.xml&#039;&#039;中的内容应该是这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Finishedpom.png]]&lt;br /&gt;
&lt;br /&gt;
通过菜单 &#039;&#039;File -&amp;gt; Save&#039;&#039;或者按住 &amp;lt;code&amp;gt;Ctrl + S&amp;lt;/code&amp;gt; 来保存&#039;&#039;pom.xml&#039;&#039;文件 .  之后, 右键项目标题，然后依次选择 &#039;&#039;Maven -&amp;gt; Update Project&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Bukkit Java文档 ===&lt;br /&gt;
&lt;br /&gt;
 如果你曾经使用过Eclipse和Java，你会知道当你将鼠标准心移至class或者 method中的代码部分时，一个含有相关文档内容的黄色小框会弹出来. 这就是（Java的）Java文档。&lt;br /&gt;
 &lt;br /&gt;
 你可以[http://download.oracle.com/javase/6/docs/api/ Oracle website 在线获取]它.。Bukkit同样拥有文档，这些文档内容包含了关于API中method和class的有用描述。&lt;br /&gt;
 &lt;br /&gt;
 你可以在[http://jd.bukkit.org/ 这]找到它 (Beta版的Java文档在[http://jd.bukkit.org/ 这]找, 新开发出来的Java文档在  &lt;br /&gt;
 &lt;br /&gt;
 [http://jd.bukkit.org/ 这]) 。为了在Eclipse里使用Java文档,这样的话当你的鼠标停留在Bukkit的class和method上时，&lt;br /&gt;
 &lt;br /&gt;
 （Bukkit的）Java文档就会冒出来。  &lt;br /&gt;
JavaDoc in spigot [https://hub.spigotmc.org/javadocs/bukkit/ Click Here]&lt;br /&gt;
&lt;br /&gt;
 首先右键在侧边栏里的&amp;quot;Maven Dependencies&amp;quot;下面的Bukkit jar,选择&amp;quot;Properties&amp;quot;.选中窗口左边的&#039;&#039;Javadoc Location&#039;&#039;, 并粘贴如下网址 &lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;http://jd.bukkit.org/&#039;&#039;&#039; (或者上面的测试版/最新版 Java文档链接也好) 到&amp;quot;Javadoc URL&amp;quot;下面的输入框内. 就像这样: &lt;br /&gt;
&lt;br /&gt;
[[Image:Bukkitjavadocs.png]] &lt;br /&gt;
&lt;br /&gt;
 点击 &amp;quot;Validate&amp;quot;,然后再点击&amp;quot;OK&amp;quot;就完成了.现在Bukkit Javadocs已经连接到Bukkit提供的资源,同时你也可以通过Eclipse来获得帮助类文档信息了.&lt;br /&gt;
&lt;br /&gt;
=== 创建一个包 ===&lt;br /&gt;
&lt;br /&gt;
 现在你需要创建一个包，它将储存所有我们需要使用的Java类文件. 右键展开&#039;&#039;src/main/java&#039;&#039;折叠并选择 &#039;&#039;New &amp;amp;gt; Package&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
[[Image:Newpackage.png]] &lt;br /&gt;
&lt;br /&gt;
 包名的话，先写上你的组名，然后加个点，再写上你的小写Artifact ID。举例, 如果你的组名是&#039;&#039;io.github.name&#039;&#039;，你的artifact名是 &#039;&#039;TestPlugin&#039;&#039;。&lt;br /&gt;
 &lt;br /&gt;
 你的包名就应该是&#039;&#039;io.github.name.testplugin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== 创建插件的类文件 ===&lt;br /&gt;
&lt;br /&gt;
既然我们已经建立好了我们的项目，我们接下来可以开始添加类文件以及制作插件了. 插件的主类指的是拓展(extends)JavaPlugin的类文件. 在你的插件中只能有一个类文件拓展JavaPlugin，无论直接还是间接. 先创建你的主类文件并保持名称与插件名一致是个很好的习惯.右键你之前新建的包,并选择&amp;amp;nbsp; &#039;&#039;New &amp;amp;gt; Class&#039;&#039;.你将会创建一个新的类文件，就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$GroupName}.{$ArtifactName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$ArtifactName} extends JavaPlugin {&lt;br /&gt;
    &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
警告： 插件绝对不应该调用自己的构造函数并实例化&lt;br /&gt;
&lt;br /&gt;
=== 创建plugin.yml ===&lt;br /&gt;
&lt;br /&gt;
你已经创建了项目和主类，如果你想要Bukkit能够加载你的插件，你还必须创建 &#039;&#039;&#039;[[Plugin YAML|plugin.yml]]&#039;&#039;&#039; 文件. 这个文件包含有基础的插件信息，如果缺失这个文件，你的插件也将会失效. 此时你需要右键&#039;&#039;src/main/resources&#039;&#039;. 选择&#039;&#039;New &amp;amp;gt; File&#039;&#039;. 命名为&amp;quot;&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;&amp;quot; 并右键完成新文件的创建.Eclipse会打开默认的文本编辑窗口来显示&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;文件中空白的内容并提供编辑. &#039;&#039;(Hint:&amp;amp;nbsp;如果你想要使得你的工作空间更加规整，关闭文本编辑器并将&#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件拖到主工作空间(拖到文件目录的右边) 之后你就可以在eclipse中编辑文件了.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
有三个基础的内容需要在plugin.yml写明. &lt;br /&gt;
: name:  插件名称. &lt;br /&gt;
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 插件主类的完整，合法名称] . &lt;br /&gt;
: version: 插件当前版本号. &lt;br /&gt;
&lt;br /&gt;
最简单的 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件内就像这样&amp;amp;nbsp;: &lt;br /&gt;
&lt;br /&gt;
 name: {插件名称}&lt;br /&gt;
 main: {包名}.{主类}&lt;br /&gt;
 version: {版本号}&lt;br /&gt;
&lt;br /&gt;
 PS:插件的包名经常会包括插件的名称，所以看到这个的时候不要感到惊讶。&lt;br /&gt;
    你的主类是否是你的插件名取决于你之前的命名方式，时刻记住这一点很重要。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;更多例子&#039;&#039;&#039;, 请看[[#Example_Files_and_Templates]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
此时你的插件已经可以被Bukkit加载了,同时服务端日志也会开始记录你的插件. 不过它现在什么用都没有!&lt;br /&gt;
&lt;br /&gt;
== onEnable()和onDisable()方法  ==&lt;br /&gt;
&lt;br /&gt;
这些方法将在插件启用与卸载时生效. 默认情况下，你的插件在被加载时会调用这些方法，所以你可以在这里注册你需要用到的事件和提供一些调试信息. &amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 方法会在插件启用时被调用, 需要包含初始化插件的逻辑语句. &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法会在插件卸载时被调用，需要包含清理(clean up)插件的逻辑语句和连接声明(associated state)。此外，插件可以重写 &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; 方法来在插件载入时执行附加的逻辑语句。&lt;br /&gt;
&lt;br /&gt;
=== onEnable()和onDisable()方法介绍  ===&lt;br /&gt;
&lt;br /&gt;
在前面的章节中，我们在主类创建了&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法. 此时，这些代码看起来就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
     @Override&lt;br /&gt;
     public void onEnable() {&lt;br /&gt;
         // 插件载入时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     @Override&lt;br /&gt;
     public void onDisable() {&lt;br /&gt;
         // 插件卸载时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这些方法已经创建，但目前它们还没有任何作用.&lt;br /&gt;
注意: 不需要添加代码来实现输出信息&amp;quot;{$PluginName} has been enabled!&amp;quot; ，因为bukkit会自动输出此类信息&lt;br /&gt;
有关更多事件的信息请查看 [[Event_API_Reference|这里]].&lt;br /&gt;
&lt;br /&gt;
=== 发送提示信息 ===&lt;br /&gt;
&lt;br /&gt;
 插件能够输出信息至控制台与服务器日志。这是通过调用插件记录器里正确的方法来实现的。首先我们必须调用 &amp;lt;code&amp;gt;getLogger()&amp;lt;/code&amp;gt; 方法来检索该插件的记录器，&lt;br /&gt;
 &lt;br /&gt;
 然后我们就可以开始记录信息了。&lt;br /&gt;
&lt;br /&gt;
我们通过调用&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法可以实现输出信息。 我们需要在&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法下面插入如下代码：&lt;br /&gt;
&lt;br /&gt;
 getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
在 &amp;lt;code&amp;gt;&#039;&#039;&#039;onDisable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法里面，你也可以做类似的事情，不过要改改输出信息。&lt;br /&gt;
&lt;br /&gt;
此时你的主类文件就像这样: &lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
         @Override&lt;br /&gt;
         public void onEnable() {&lt;br /&gt;
             getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public void onDisable() {&lt;br /&gt;
         getLogger().info(&amp;quot;onDisable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 防止重载后插件失效 ===&lt;br /&gt;
&lt;br /&gt;
你需要知道的是，插件并不只会伴随服务器关闭、启动而重载，你的插件也可能会被其他插件启用、关闭，或是在服务器运行时使用/reload命令. &lt;br /&gt;
假设服务器启动之后一会儿后，你的插件才被启用，这时便是危险的,因为服务器中可能已经有玩家在线、额外的世界被加载以及很多预想不到的不同之处(与&amp;quot;插件同服务器同时加载&amp;quot;的情况相比较). &lt;br /&gt;
&lt;br /&gt;
举个例子来说:&lt;br /&gt;
* 你有一个用来储存HashMap中的玩家登入信息的插件&lt;br /&gt;
* 你希望该信息对所有玩家都可见&lt;br /&gt;
* 一名管理员使用/reload命令&lt;br /&gt;
* 你的插件被卸载，所有储存的数据都丢失了&lt;br /&gt;
* 你的插件被再次启用时，一些玩家已经在线&lt;br /&gt;
* 这些玩家在HashMap并没有储存的信息&lt;br /&gt;
* 你试图重新获得他们的信息，但是失败了!&lt;br /&gt;
&lt;br /&gt;
为了插件重载以后能够正常工作, 你需要在插件启用时加载所有已经在线的玩家的信息并将之储存在HashMap中.&lt;br /&gt;
&lt;br /&gt;
 for (Player player : Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
     playerList.put(player.getName(), playerData(player));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== 监听器  ==&lt;br /&gt;
监听器是一种方法被调用来对事件作出反应的类.所有的监听器使用 org.bukkit.event.Listener接口.更多有关监听器创建的细节,&lt;br /&gt;
:: &#039;&#039; 请查看: [[Event API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 指令  ==&lt;br /&gt;
&lt;br /&gt;
=== onCommand() 方法  ===&lt;br /&gt;
&lt;br /&gt;
 现在你已经知道如何注册一个事件并做出响应,但是如果你只是想要在命令输入之后做出响应呢? 你可以使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 这个代码的作用是当玩家输入“/”时，监听该操作并执行相关语句&amp;quot;/&amp;quot; . 举个例子来说.输入 &amp;quot;/do something&amp;quot; 将会执行 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 此种情况下，因为没有特定的行为被编程，所以并不会发生任何事情。&lt;br /&gt;
&lt;br /&gt;
 请避免使用和bukkit所提供的指令重名的指令, 然后深思你的指令名的唯一性。 &lt;br /&gt;
 &lt;br /&gt;
 例如.指令&amp;quot;give&amp;quot;已经被好几个插件使用了, 如果你执行了另外一个&amp;quot;give&amp;quot;指令, 你的插件将会和这些插件冲突。&lt;br /&gt;
&lt;br /&gt;
 你必须在插件的&amp;quot;plugins.yml&amp;quot;注册你的指令 否则这个方法将不会被触发。&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法必须返回一个布尔值（true或false）。 &lt;br /&gt;
 &lt;br /&gt;
 如果返回值是true，你不会看到什么明显的事情发生。 &lt;br /&gt;
 &lt;br /&gt;
 但如果返回值是false，则会返回你的plugin.yml里的&#039;usage:property&#039;然后发送给命令使用者. &lt;br /&gt;
&lt;br /&gt;
当使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时, 你需要填写4个参数. &lt;br /&gt;
&lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 发送命令的对象&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;Command cmd&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行的指令&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String commandLabel&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行指令的别名&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String[] args&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 该指令的自变量数组。&lt;br /&gt;
 &lt;br /&gt;
例如.指令 &#039;&#039;/hello abc def&#039;&#039; 中， &#039;&#039;abc&#039;&#039; 会被存放进args[0]中, &#039;&#039;def&#039;&#039; 被存放进args[1]中。&lt;br /&gt;
&lt;br /&gt;
==== 设置命令  ====&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { // 如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
     // 所需要执行的事（此处略）&lt;br /&gt;
         return true;&lt;br /&gt;
     } //如果以上内容成功执行，则返回true &lt;br /&gt;
     // 如果执行失败，则返回false.&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 每当使用&amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时， 在每个方法后面都加上一个 return false; 是一个惯例。 &lt;br /&gt;
 &lt;br /&gt;
 返回false会显示在plugin.yml里设置的usage信息 (看下面). 用这种方法的话，一旦执行出错便会显示usage信息。&lt;br /&gt;
&lt;br /&gt;
 每当方法return一个值的时候，这个方法就会退出，所以return true的时候，在它下面的代码就会被跳过, &lt;br /&gt;
 &lt;br /&gt;
 除非return语句在一个if的嵌套中，或者类似的嵌套情况。 &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;.equalsIgnoreCase(&amp;quot;basic&amp;quot;)&#039;&#039;&#039;&amp;lt;/code&amp;gt;代表忽略英文大小写. 在这种情况下,字符串&amp;quot;BAsIc&amp;quot; 和 &amp;quot;BasiC&amp;quot;和 basic相同，代码会被照常执行。&lt;br /&gt;
&lt;br /&gt;
和以前一样，加两行import在你的java文件头&lt;br /&gt;
&lt;br /&gt;
 import org.bukkit.command.Command;&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.command.CommandSender;&lt;br /&gt;
&lt;br /&gt;
=== 在Plugin.yml中添加你的指令  ===&lt;br /&gt;
&lt;br /&gt;
你需要添加你的指令到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件里. 如下是指令/basic添加到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039;的例子（请在plugins.yml的末尾添加如下代码）: &lt;br /&gt;
&lt;br /&gt;
commands:&lt;br /&gt;
   basic:&lt;br /&gt;
      description: This is a demo command.&lt;br /&gt;
      usage: /&amp;lt;command&amp;gt; [player]&lt;br /&gt;
      permission: &amp;lt;plugin name&amp;gt;.basic&lt;br /&gt;
      permission-message: You don&#039;t have &amp;lt;permission&amp;gt;&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;basic&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令名称&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;description&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令描述&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;usage&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 在oncommand方法return false后显示的用法提示。尽量简洁, 使别人能够理解指令是什么以及如何使用它。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被一些显示帮助信息的插件使用，用来决定向玩家显示哪个指令（完全不对劲吧！）。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission-message&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 当玩家使用了这个指令而没有权限时输出的信息。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 PS:yml文件使用两个空格作为制表符,使用tab键输入制表符会导致错误。&lt;br /&gt;
&lt;br /&gt;
===控制台指令vs玩家指令===&lt;br /&gt;
&lt;br /&gt;
 你可能注意到了上文的&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt;参数. &amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt; 是个Bukkit接口，&lt;br /&gt;
 &lt;br /&gt;
 它有两个（对插件编写者）有用的子类：&amp;lt;code&amp;gt;&#039;&#039;&#039;Player&#039;&#039;&#039;&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;&#039;&#039;&#039;ConsoleCommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 所以当你编写插件的时候, 确定这个指令从控制台发出后能完全正常工作是十分必要的,&lt;br /&gt;
 &lt;br /&gt;
 那些只能由在线玩家执行的指令 &#039;&#039;只能&#039;&#039; 由一个在线玩家执行。&lt;br /&gt;
 &lt;br /&gt;
 有的插件在判断命令执 行对象不是玩家时仅仅用return进行处理 (比如当有人从控制台发出指令), &lt;br /&gt;
 &lt;br /&gt;
 即便这些指令能够在控制台完美运行（比如改变天气的指令）。&lt;br /&gt;
&lt;br /&gt;
一个判断sender的方法: &lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { //如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
             // 所需要执行的事（此处略）&lt;br /&gt;
             return true;&lt;br /&gt;
         } else if (cmd.getName().equalsIgnoreCase(&amp;quot;basic2&amp;quot;)) {&lt;br /&gt;
             if (!(sender instanceof Player)) { //如果sender与Player类不匹配&lt;br /&gt;
                 sender.sendMessage(&amp;quot;This command can only be run by a player.&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 Player player = (Player) sender;&lt;br /&gt;
                    // 所需要执行的事（此处略）&lt;br /&gt;
             }&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 在这个例子里，指令&#039;&#039;&#039;basic&#039;&#039;&#039;能由任何人发出 - 一个在线的玩家, 或者服务器后台的op. &lt;br /&gt;
 &lt;br /&gt;
 但是指令&#039;&#039;&#039;basic2&#039;&#039;&#039;只能由一个在线的玩家发出. &lt;br /&gt;
&lt;br /&gt;
 大体上说,你应该允许尽可能多的指令在控制台和玩家聊天框里都正常执行. &lt;br /&gt;
 &lt;br /&gt;
 需要在线玩家执行的指令可以使用以上例子中的机制来检测&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;是一个玩家. &lt;br /&gt;
 &lt;br /&gt;
 很多指令广泛依靠于一些玩家的属性（例如可以被tp或被给予物品等等）。&lt;br /&gt;
&lt;br /&gt;
 如果你想更进一步, 你可以对你的指令的自变量做一些额外的检测， 例如传送指令在提供玩家ID的情况下（也只有在这种情况下）可以在控制台被执行。&lt;br /&gt;
&lt;br /&gt;
=== 使用独立的 CommandExecutor class  ===&lt;br /&gt;
&lt;br /&gt;
 以上的例子必须把 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法放在插件的主类里。&lt;br /&gt;
 &lt;br /&gt;
 对于小插件来说，这是极好的,但如果你要写一个大点的插件,把你的 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法放在独立的类里将会很有意义。&lt;br /&gt;
 &lt;br /&gt;
 幸运的是，这并不难: &lt;br /&gt;
&lt;br /&gt;
*在你的包里创建一个新类. 命名为&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;之类的 (当然，要把&#039;&#039;&#039;MyPlugin&#039;&#039;&#039;替换成你制作的插件的名字)。 这个类 &#039;&#039;&#039;必须&#039;&#039;&#039; 继承Bukkit 的&#039;&#039;&#039;CommandExecutor&#039;&#039;&#039;接口。&lt;br /&gt;
&lt;br /&gt;
*在你的插件的&amp;lt;code&amp;gt;&#039;&#039;&#039;onEnable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法中,你需要实例化你创建的命令执行类(CommandExecutor class), 然后做一个像下面一样的调用：&amp;lt;code&amp;gt;getCommand(&amp;quot;basic&amp;quot;).setExecutor(myExecutor);&amp;lt;/code&amp;gt;&lt;br /&gt;
 其中&amp;quot;basic&amp;quot;是我们需要操控的指令, &amp;lt;code&amp;gt;myExecutor&amp;lt;/code&amp;gt; 是我们创建的实例.&lt;br /&gt;
&lt;br /&gt;
废话少说直接吔我例子: &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyPlugin.java&#039;&#039;&#039; (插件的主类): &lt;br /&gt;
 @Override&lt;br /&gt;
 public void onEnable() {&lt;br /&gt;
         // 如果你没有在plugins.yml注册过指令的话，此处会抛出空指针异常！&lt;br /&gt;
         this.getCommand(&amp;quot;basic&amp;quot;).setExecutor(new MyPluginCommandExecutor(this));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;MyPluginCommandExecutor.java&#039;&#039;&#039;（独立CommandExecutor类）: &lt;br /&gt;
 public class MyPluginCommandExecutor implements CommandExecutor {&lt;br /&gt;
         private final MyPlugin plugin;&lt;br /&gt;
 &lt;br /&gt;
         public MyPluginCommandExecutor(MyPlugin plugin) {&lt;br /&gt;
             this.plugin = plugin; // Store the plugin in situations where you need it.&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
             // 和以前一样执行就好…&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 请注意我们是如何把主类里的对象指向&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;的. 这允许我们轻易的使用主类对象的方法（如果我们需要的话）。&lt;br /&gt;
&lt;br /&gt;
 这么做的话, 我们便能更好的组织代码 - 如果 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法又大又复杂, 它就能被划分为子类，从而不使插件的主类显得凌乱。&lt;br /&gt;
&lt;br /&gt;
 PS：如果你的插件含有多个指令的话, 你需要为每一个指令单独设立一个commandexecutor.&lt;br /&gt;
&lt;br /&gt;
=== 写一个安全的指令 ===&lt;br /&gt;
 当写一个指令的时候,别进行任何假设（很重要）, 例如假设执行者一定是个玩家. 请牢记以下原则:&lt;br /&gt;
&lt;br /&gt;
==== 在命令执行前确定发出者是玩家 ====&lt;br /&gt;
&lt;br /&gt;
使用简单的例子来进行检测:&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (sender instanceof Player) {&lt;br /&gt;
             Player player = (Player) sender;&lt;br /&gt;
             // do something&lt;br /&gt;
         } else {&lt;br /&gt;
             sender.sendMessage(&amp;quot;You must be a player!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
             // do something&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 检测自变量个数 ====&lt;br /&gt;
&lt;br /&gt;
 别老是认为玩家都能打对正确数量的自变量. &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (args.length &amp;gt; 4) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Too many arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         } &lt;br /&gt;
         if (args.length &amp;lt; 2) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Not enough arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 在获取玩家之前先检测他们是否在线 ====&lt;br /&gt;
&lt;br /&gt;
 有时你想通过命令发出者打出的ID来获取其他玩家，请确保他们在线再说！&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         Player target = (Bukkit.getServer().getPlayer(args[0]));&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not online!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 如果你想对离线玩家进行操作，&amp;lt;code&amp;gt;&#039;&#039;&#039;OfflinePlayer&#039;&#039;&#039;&amp;lt;/code&amp;gt;类提供了基础操作的方法.&lt;br /&gt;
&lt;br /&gt;
== 插件的配置/设置  ==&lt;br /&gt;
Bukkit API 为插件提供了一种简便的方法，用来管理用户自定义的设置。同时，通过这种方法也可以储存数据。&lt;br /&gt;
:: &#039;&#039;详情请查看: [[Configuration API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 权限  ==&lt;br /&gt;
&lt;br /&gt;
利用新的Bukkit API来设置权限是再容易不过的事情. 检测玩家是否拥有权限，可以使用以下代码: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;if (player.hasPermission(&amp;quot;some.pointless.permission&amp;quot;)) {&lt;br /&gt;
   //执行此段代码(有权限)&lt;br /&gt;
} else {&lt;br /&gt;
   //执行此段代码(无权限)&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你也可以检测一个权限节点是否被定义过，(等效于 Java 语言中的&#039;&#039;&#039;null&#039;&#039;&#039;)，使用下面的方法: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;boolean isPermissionSet(String name)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你或许对此抱有疑惑:为什么这段代码中没有任何用户群的信息？因为它们并不是必需的. 之前这段代码想要实现的主要功能之一就是根据用户群格式化聊天信息. 但其实使用权限就可以很容易地完成这项工作.你可以在聊天插件的配置文件中定义权限与前缀的对应关系. 举例来说，&amp;quot;someChat.prefix.admin&amp;quot; 权限可以对应前缀[Admin].当拥有此权限的玩家发出聊天信息时 ，聊天前缀便是[Admin]. &lt;br /&gt;
&lt;br /&gt;
另一个想要实现的主要功能则是给所有拥有权限的在线用户发一条信息.你可以这样做: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;for (Player player: Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
    if (player.hasPermission(&amp;quot;send.receive.message&amp;quot;)) {&lt;br /&gt;
        player.sendMessage(&amp;quot;You were sent a message&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
看到这里你可能会问，若是没有权限组我该如何配置玩家的 [[权限]] ？既然 [[Bukkit]] API 本身并没有提供用户组的预设, 你必须通过安装一款权限组插件，像 [[permissionsBukkit]]  这样的插件来帮助你配置用户群. &#039;&#039;&#039;API 只是提供接口而不是提供已实现的类.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
=== 配置权限 ===&lt;br /&gt;
{{Hide|标题=译者注 |内容= 其一，以下代码中部分内容将以&#039;&#039;//文字&#039;&#039;翻译，使用时请不要加入其中内容，仅做教程中的翻译参考；&lt;br /&gt;
其二，default:true在下面翻译为全员默认拥有该权限，default:false则翻译为全员默认没有该权限，请注意理解。 }}&lt;br /&gt;
如果你想要更多地控制你的权限，例如设置权限的默认值或是子权限节点，不如将权限节点加入你的 &#039;&#039;[[plugin.yml]]&#039;&#039;文件.这并不是必须的，但我们推荐你这样做. 下面是一个实例，显示了当你将权限添加在&#039;&#039;plugin.yml&#039;&#039;文件末尾时的样子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands //给予使用doorman所有命令的权限&lt;br /&gt;
        children:  //子权限节点&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&lt;br /&gt;
    doorman.kick:&lt;br /&gt;
        description: Allows you to kick a user  //允许你踢出一名用户&lt;br /&gt;
        default: op  //OP默认拥有该权限&lt;br /&gt;
    doorman.ban:&lt;br /&gt;
        description: Allows you to ban a user  //允许你封禁一名用户&lt;br /&gt;
        default: op //OP默认拥有该权限&lt;br /&gt;
    doorman.knock:&lt;br /&gt;
        description: Knocks on the door! //敲门&lt;br /&gt;
        default: true //全员默认拥有该权限&lt;br /&gt;
    doorman.denied:&lt;br /&gt;
        description: Prevents this user from entering the door  //禁止拥有该权限节点的用户进入门内&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
值得注意的是,你的插件使用的每个权限都被定义为 &#039;&#039;permissions&#039;&#039; 这个节点的子权限节点. 每个权限都能够拥有一个描述，默认值以及子权限节点。&lt;br /&gt;
==== 默认值  ====&lt;br /&gt;
&lt;br /&gt;
当一个权限没有明确是否为玩家所拥有&amp;amp;nbsp;，&#039;&#039;hasPermission&#039;&#039;&amp;lt;ref&amp;gt;这是一个检测玩家是否拥有指定权限节点的方法&amp;lt;/ref&amp;gt;&amp;amp;nbsp;将返回false&amp;lt;ref&amp;gt;该方法返回的boolean值&amp;lt;/ref&amp;gt;. 在你的[[plugin.yml]] you can change this by setting the default node to be one of four values: &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;true&#039;&#039;&#039; - 全员默认拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;false&#039;&#039;&#039; - 全员默认没有该权限. &lt;br /&gt;
*&#039;&#039;&#039;op&#039;&#039;&#039; - 如果玩家是一名OP&amp;lt;ref&amp;gt;Operator，服务器管理员&amp;lt;/ref&amp;gt;，那么他将拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;not op&#039;&#039;&#039; - 如果玩家不是一名OP，那么他将拥有该权限.&lt;br /&gt;
&lt;br /&gt;
==== 子权限  ====&lt;br /&gt;
&lt;br /&gt;
在这之前你可能已经习惯了通过权限节点自动分配所有的子权限&amp;lt;ref&amp;gt;原句:Before now you will probably be used to the * permission to automatically assign all sub permissions.此处的sub permissions应是同上文“你的插件使用的每个权限都被定义为 permissions 这个节点的子权限节点.”相照应&amp;lt;/ref&amp;gt;. 现在这种方法已经随着bukkit API&amp;lt;ref&amp;gt;应用程序编程接口(Application Programming Interface)&amp;lt;/ref&amp;gt;的更新而改变，现在你可以自己定义子权限了. 这将使得插件具有更多的灵活性. 下面是一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands  //翻译见上&lt;br /&gt;
        children:&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
在这里，&#039;&#039;doorman.*&#039;&#039; 权限拥有几个子权限并分配在其下. 子权限工作的方式就是当&#039;&#039;doorman.*&#039;&#039; 权限节点被设置为true时,子权限也会随之设置为 &#039;&#039;plugin.yml&#039;&#039;中的默认值. 然而当&#039;&#039;doorman.*&#039;&#039; 被设置为false时，所有子权限都会设置为与默认值相反的值（true-&amp;gt;false,false-&amp;gt;true）.&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设置你自己的权限  ===&lt;br /&gt;
&lt;br /&gt;
如果你希望想知道更多有关开发你自己的权限插件的事情(即是设置权限的管理类插件) ，那么请前往查看教程&amp;amp;nbsp;[[Developing a permissions plugin]].&lt;br /&gt;
&lt;br /&gt;
== Scheduling Tasks and Background Tasks  ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 方块操控&amp;lt;br&amp;gt;  ==&lt;br /&gt;
&lt;br /&gt;
这是一个很容易的方法创建、获取或修改一个方块, 例如当你想要修改往你头上数第五格的方块时, 首先你需要做的是获取你当前的坐标点, 然后往你当前的坐标点的Y轴加上五, 获取目标方块后我们便能够很容易地修改它了, 例子如下:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@EventHandler&lt;br /&gt;
public void onPlayerMove(PlayerMoveEvent event) {&lt;br /&gt;
    // 得到玩家当前的坐标.&lt;br /&gt;
    Location loc = event.getPlayer().getLocation();&lt;br /&gt;
    // 设置目标坐标为玩家头顶 5 格处.&lt;br /&gt;
    loc.setY(loc.getY() + 5);&lt;br /&gt;
    // 得到目标坐标的方块&lt;br /&gt;
    Block b = loc.getBlock();&lt;br /&gt;
    // 将目标坐标的方块更改为石头 (stone).&lt;br /&gt;
    b.setType(Material.STONE);&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
上面的代码获取了玩家的所在位置，获取头上的第五个方块，并且放上一个石头（stone）。 Note that once you have a &amp;lt;code&amp;gt;Block&amp;lt;/code&amp;gt;, there are other things you can do besides set its type. 你可以查阅JavaDoc获取更多的东西。&lt;br /&gt;
&lt;br /&gt;
你可以用这样相似的观念去生成一些建筑，并且还可以用编程的方式通过算法生成一些独特的方块（这里指由很多个方块拼在一起的四四方方的方块）。比如，去生成一个实心的方块建筑，你可能在生成建筑的时候还需要用到&amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;循环，去一遍遍循环来生成这些建筑，填满这些建筑等。&lt;br /&gt;
下面，是一个在指定坐标生成一个实心的方块建筑的实例方法：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void generateCube(Location loc, int length) {&lt;br /&gt;
    // 放置这个方块最中间的方块并且获取这个方块的坐标。&lt;br /&gt;
    // Uses getBlockN() instead of getN() to avoid casting to an int later.&lt;br /&gt;
    int x1 = loc.getBlockX(); &lt;br /&gt;
    int y1 = loc.getBlockY();&lt;br /&gt;
    int z1 = loc.getBlockZ();&lt;br /&gt;
&lt;br /&gt;
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.&lt;br /&gt;
    int x2 = x1 + length;&lt;br /&gt;
    int y2 = y1 + length;&lt;br /&gt;
    int z2 = z1 + length;&lt;br /&gt;
&lt;br /&gt;
    World world = loc.getWorld();&lt;br /&gt;
&lt;br /&gt;
    // Loop over the cube in the x dimension.&lt;br /&gt;
    for (int xPoint = x1; xPoint &amp;lt;= x2; xPoint++) { &lt;br /&gt;
        // Loop over the cube in the y dimension.&lt;br /&gt;
        for (int yPoint = y1; yPoint &amp;lt;= y2; yPoint++) {&lt;br /&gt;
            // Loop over the cube in the z dimension.&lt;br /&gt;
            for (int zPoint = z1; zPoint &amp;lt;= z2; zPoint++) {&lt;br /&gt;
                // Get the block that we are currently looping over.&lt;br /&gt;
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);&lt;br /&gt;
                // 设置方块ID为57号(钻石块!)&lt;br /&gt;
                currentBlock.setType(Material.DIAMOND_BLOCK); //这里下面会提到&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
这个方法将会在程序给予这个方法高度和起始坐标（最中间的方块的坐标）时，构建一个3D的方块建筑。简单的删除方块也可以使用这个方法的思路实现，但是，但是设置方块的时候，记得把上面标注会提到的那行，设置为物品ID为0的方块(空气方块)。&amp;lt;br&amp;gt; &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== (玩家)操控背包  ==&lt;br /&gt;
&lt;br /&gt;
这个部分大部分讲的是操控玩家的背包。但是在某些情况下，操控箱子里的内容与此相同，当然得在你能找到获取箱子里内容的方法才可以。&amp;amp;nbsp;:P. 这是一个操控玩家背包的一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void onPlayerJoin(PlayerJoinEvent evt) {&lt;br /&gt;
    Player player = evt.getPlayer(); // 当玩家加入游戏&lt;br /&gt;
    PlayerInventory inventory = player.getInventory(); // 获取玩家背包列表&lt;br /&gt;
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // 生成一组钻石&lt;br /&gt;
        &lt;br /&gt;
    if (inventory.contains(itemstack)) {&lt;br /&gt;
        inventory.addItem(itemstack); // 将一组钻石放到玩家的背包里&lt;br /&gt;
        player.sendMessage(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;); //向玩家发送消息(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
所以，上面代码里面的 onPlayerJoin 方法中，我们首先构建了一些变量，让我们的工作变得更容易一些。变量分别是： player, inventory and itemstack。 inventory 变量是玩家的背包内容，itemstack是那64个钻石的物品栈。然后我们判断了玩家是不是有一组钻石。如果有，我们就用inventory.addItem(itemstack)方法再给玩家一组钻石，并且给他发送一个消息“Wow！你看上去很土豪啊！”。 所以操控背包并不像我们想象的那么难，如果我们想实现让玩家进入游戏，检测到一组钻石后删掉这组钻石，只需要把代码里的inventory.addItem(itemstack)替换成inventory.remove(itemstack)。或许你还需要把“Wow！你看上去很土豪啊！”改成“Wow！你看上去好穷啊！”2333333。&lt;br /&gt;
&lt;br /&gt;
== 控制物品 ==&lt;br /&gt;
&lt;br /&gt;
当你需要写一个对物品们做出行为的代码时，你要使用ItemStack类来查阅有关那个栈的所有设置信息。&lt;br /&gt;
&lt;br /&gt;
=== 附魔属性 ===&lt;br /&gt;
&lt;br /&gt;
你想给一个物品附魔，你首先需要了解[http://www.minecraftwiki.net/wiki/Data_values Item Code]和[http://www.minecraftwiki.net/wiki/Talk:Enchanting#Effect_IDs Effect ID].  Enchantments他们自己不能例示(new Enchantment() won&#039;t work) 因为他们是抽象的。所以你必须要用一个EnchantmentWrapper。如果你想去附魔物品，还不想在默认的SMP里附魔，可以使用addEnchantment()里面的addUnsafeEnchantment()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
int itemCode = 280;  //use the item code you want here&lt;br /&gt;
int effectId = 20;  //use the enchantment code you want here&lt;br /&gt;
int enchantmentLevel = 100;&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(itemCode);  //new item of item code&lt;br /&gt;
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id&lt;br /&gt;
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //给物品附魔&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ItemMeta ===&lt;br /&gt;
You can set the display name of an item by doing this.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
String myDisplayName = &amp;quot;Awesome Sword&amp;quot;; //这里是保存了你想要显示的名字&lt;br /&gt;
 &lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你要更改的方块&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //得到方块的元数据&lt;br /&gt;
im.setDisplayName(myDisplayName); //设置它显示的名字&lt;br /&gt;
myItem.setItemMeta(im); //保存更改&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
You can also set the lores of an item. The lores are the small annotations on an item, like &amp;quot;+5 attack damage&amp;quot; on a stone sword.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;String&amp;gt; lores = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
lores.add(&amp;quot;Example lore&amp;quot;, &amp;quot;this one comes on line 2&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again&lt;br /&gt;
im.setLore(lores); //add the lores of course&lt;br /&gt;
myItem.setItemMeta(im); //give the item the new itemmeta&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maps, and Sets, and Lists, Oh My! &amp;lt;ref&amp;gt;标题中Map,Set,List均为Java类集中的接口，故不翻译&amp;lt;/ref&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
除了Map类和HashMap类，Java中还提供了其它的数据结构类集。 &lt;br /&gt;
&lt;br /&gt;
他们提供这些不同的类，是因为有些时候，使用Map类并不合适。&amp;lt;nowiki&amp;gt;[[Java data structure classes]]&amp;lt;/nowiki&amp;gt;在这个条目中，有对Java数据框架类集更深入的讲述。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;=== HashMaps概览以及如何使用此接口  ===&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;Keep in mind to never use a player in a hashmap!&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;&amp;lt;ref&amp;gt;下面有讲解。&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
应在HashMap中使用字符串.，使用 &amp;quot;p.getName()&amp;quot; 来添加，移除或是检查列表中是否含有某个玩家.&lt;br /&gt;
&lt;br /&gt;
将玩家当作object储存在HashMap中会造成大量内存泄漏.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;adam&amp;quot; as the key and assigned a value of &amp;quot;a&amp;quot; to it. That would work as intended, but then say afterwards I wanted to assign the value of &amp;quot;b&amp;quot; to key &amp;quot;adam&amp;quot; I would be able to and would get no errors but the value of &amp;quot;a&amp;quot; assigned to key &amp;quot;adam&amp;quot; in the HashMap would be overwritten because HashMaps cannot contain duplicate values. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Defining a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public Map&amp;lt;KeyType, DataType&amp;gt; HashMapName = new HashMap&amp;lt;&amp;gt;(); //Example syntax&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Example Declaration&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; pluginEnabled = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; isGodMode = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So inside on command you&#039;ll need this, the function name can be different but for the sake of simplicity it&#039;s best if you keep it the same. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;Player player = (Player) sender;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
togglePluginState(player);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void togglePluginState(Player player) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Notice how we use the player name as the key here,&lt;br /&gt;
&lt;br /&gt;
    // not the player object&lt;br /&gt;
&lt;br /&gt;
    String playerName = player.getName();&lt;br /&gt;
&lt;br /&gt;
    if (pluginEnabled.containsKey(playerName)) {&lt;br /&gt;
&lt;br /&gt;
        if (pluginEnabled.get(playerName)) {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, false);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin disabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, true);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    } else {&lt;br /&gt;
&lt;br /&gt;
        pluginEnabled.put(playerName, true); //If you want plugin disabled by default change this value to false.&lt;br /&gt;
&lt;br /&gt;
        player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== More Ideas for HashMaps  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A HashMap (or really any kind of Map in Java) is an association. It allows quick and efficient lookup of some sort of &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;value&#039;&#039;&#039;, given a unique &#039;&#039;&#039;key&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;. Anywhere this happens in your code, a Map may be your solution. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here are a few other ideas which are ideally suited to using Maps. As you will see, it doesn&#039;t have to be data that you store per player, but can be any kind of data that needs to be &amp;quot;translated&amp;quot; from one form to another. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Data Value Lookups  =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Integer&amp;gt; wool_colors = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;orange&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;magenta&amp;quot;, 2);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;light blue&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
   ..&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;black&amp;quot;, 15);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this in response to user commands - turn &amp;quot;green&amp;quot; into 13&lt;br /&gt;
&lt;br /&gt;
int datavalue = 0;&lt;br /&gt;
&lt;br /&gt;
if (wool_colors.containsKey(argument)) {&lt;br /&gt;
&lt;br /&gt;
    datavalue = wool_colors.get(argument);&lt;br /&gt;
&lt;br /&gt;
} else {&lt;br /&gt;
&lt;br /&gt;
    try { datavalue = Integer.parseInt(argument); }&lt;br /&gt;
&lt;br /&gt;
    catch (Exception e) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Saving/Loading a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you don&#039;t want an administrator to edit the data manually&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you need to save data in binary format (too complex to organize for YAML)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you want to avoid parsing block names and/or other objects from freeform text&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is very simple way how to save any HashMap. You can replace HashMap&amp;amp;amp;lt;String, Integer&amp;amp;amp;gt; with any type of HashMap you want. Let&#039;s take an example HashMap with code to save it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        HashMap&amp;lt;String, Integer&amp;gt; mapToSave = new HashMap&amp;lt;String,Integer&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
        public void save(HashMap&amp;lt;String, Integer&amp;gt; map, String path) {&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(map);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
//Handle I/O exceptions&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
save(mapToSave, getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see it&#039;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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public HashMap&amp;lt;String, Integer&amp;gt; load(String path) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
Object result = ois.readObject();&lt;br /&gt;
&lt;br /&gt;
//you can feel free to cast result to HashMap&amp;lt;String, Integer&amp;gt; if you know there&#039;s that HashMap in the file&lt;br /&gt;
&lt;br /&gt;
return (HashMap&amp;lt;String, Integer&amp;gt;)result;&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HashMap&amp;lt;Integer, String&amp;gt; loadedMap;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
String path = getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
File file = new File(path);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if (file.exists()) { // check if file exists before loading to avoid errors!&lt;br /&gt;
&lt;br /&gt;
loadedMap  = load(path);&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
You can use this &amp;quot;API&amp;quot; for saving/loading HashMaps, ArrayLists, and all Objects which implement Serializable or Externalizable interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;/** SLAPI = Saving/Loading API&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 * API for Saving and Loading Objects.&lt;br /&gt;
&lt;br /&gt;
 * Everyone has permission to include this code in their plugins as they wish :)&lt;br /&gt;
&lt;br /&gt;
 * @author Tomsik68&amp;lt;tomsik68@gmail.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
public class SLAPI&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; void save(T obj,String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(obj);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; T load(String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
T result = (T)ois.readObject();&lt;br /&gt;
&lt;br /&gt;
ois.close();&lt;br /&gt;
&lt;br /&gt;
return result;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Example implementation of this API: &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;I&#039;m skipping some part of code in this source&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public class Example extends JavaPlugin {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
private ArrayList&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onEnable() {&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
list = SLAPI.load(&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                //handle the exception&lt;br /&gt;
&lt;br /&gt;
                e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onDisable()	{&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
SLAPI.save(list,&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                 e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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&#039;re writing your own data object classes, and you may want to save their state using this technique, you should read about Java&#039;s Serializable or Externalizable interface. The only difference between Externalizable and Serializable is, that Serializable automatically takes all of class&#039;s fields and tries to serialize them, while Externalizable allows you to define method for reading and writing the Object. It&#039;s easy to add to your code, and it will make your data persistent with very little work required. No more parsing!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note #2: This API doesn&#039;t support changes. Once you change something in the class, data files saved with older version of your plugin won&#039;t load correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Tips &amp;amp; Examples =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.) Simplify your save structure&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.) Save version number along with data&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always remember, that you don&#039;t know what you&#039;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&#039;t know!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.) Migrate older files&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your plugin finds older version of some file, it should update the file accordingly and change version number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 元数据  ==&lt;br /&gt;
&lt;br /&gt;
Bukkit正在尝试尽可能的让制作插件变得更简单，所以带有一个键值例如Player, Entity, World，甚至是一个block的HashMaps都可以用元数据取缔掉。元数据是一些替代HashMap的方式。他可以允许你去自定义&amp;quot;fields&amp;quot;给Players, Entities, Worlds and Blocks.这些东西都是元数据类的一个成员(详见[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&#039;re creating an economy plugin, you would need a HashMap of Player and Float or Double. 如果用了元数据，你不必去做! You just attach to player new metadata 值, and that&#039;s it!&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== 为何要去使用元数据  ===&lt;br /&gt;
&lt;br /&gt;
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps. &lt;br /&gt;
* 元数据可以用来跟其他插件分享信息与数据。&lt;br /&gt;
&lt;br /&gt;
=== 为什么不去使用元数据&amp;lt;br&amp;gt;  ===&lt;br /&gt;
&lt;br /&gt;
* Slightly more difficult to get the value.&lt;br /&gt;
* It is not saved on shutdown (but then again, neither are any Maps that you create).&lt;br /&gt;
&lt;br /&gt;
=== 获取 &amp;amp;amp; 设置元数据  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {&lt;br /&gt;
  object.setMetadata(key, new FixedMetadataValue(plugin,value));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Object getMetadata(Metadatable object, String key, Plugin plugin) {&lt;br /&gt;
  List&amp;lt;MetadataValue&amp;gt; values = object.getMetadata(key);  &lt;br /&gt;
  for (MetadataValue value : values) {&lt;br /&gt;
     // Plugins are singleton objects, so using == is safe here&lt;br /&gt;
     if (value.getOwningPlugin() == plugin) {&lt;br /&gt;
        return value.value();&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
  return null;&lt;br /&gt;
}&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note: If you&#039;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.&lt;br /&gt;
&lt;br /&gt;
== Databases  ==&lt;br /&gt;
&lt;br /&gt;
Sometimes flat files aren&#039;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).&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there&#039;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!) &lt;br /&gt;
&lt;br /&gt;
=== SQLite  ===&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;s very straightforward and shouldn&#039;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]. &lt;br /&gt;
&lt;br /&gt;
SQLite is great for very simple databases, because there&#039;s no server concerns to set up. Just make a few calls to create a new database and table. It&#039;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&#039;s often easiest and fastest to get the SQL basics squared away with SQLite, even if you &amp;quot;graduate&amp;quot; to a more server-class database engine later.&lt;br /&gt;
&lt;br /&gt;
=== MySQL  ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 部署你的插件  ==&lt;br /&gt;
&lt;br /&gt;
 总之你写好的你的插件,如何把它变成一个可安装在服务端的jar文件呢?&lt;br /&gt;
 &lt;br /&gt;
 首先我们需要建立一个水桶服务器。 [http://wiki.bukkit.org/Setting_up_a_server 在这里] 能找到开服相关信息.接下来你需要把插件导出为一个.jar文件，&lt;br /&gt;
 &lt;br /&gt;
 这样你就能在你的服务器里运行它。 在Eclipse里, 右键工程，点击&#039;&#039;Run as &amp;gt; Maven install&#039;&#039;即可:&lt;br /&gt;
&lt;br /&gt;
[[Image:Maveninstall.png]]&lt;br /&gt;
&lt;br /&gt;
 将来呢, 当你对插件进行了改动后,在做上述操作之前，如果你想删除之前的.jar文件，你可以右键工程，点击 &#039;&#039;Run as &amp;gt; Maven clean&#039;&#039; 。 &lt;br /&gt;
 &lt;br /&gt;
 如果你在编译插件时遇到了问题,  请检查你的JDK是否正确安装， 并浏览[[建立工作区]]条目。&lt;br /&gt;
 &lt;br /&gt;
 如果你在Eclipse控制台看到了和JDK有关的错误，你可能需要手动调整JDK,因为Eclipse的检测可能发生了错误。 &lt;br /&gt;
 &lt;br /&gt;
 点击&#039;&#039;Window -&amp;gt; Preferences&#039;&#039;, 然后是&#039;&#039;Java -&amp;gt; Installed JREs&#039;&#039;。选中你最近安装的JDK作为Java运行环境（JRE）并添加, 并取消掉原来那个报错的JDK:&lt;br /&gt;
&lt;br /&gt;
[[Image:Jrelocation.png]]&lt;br /&gt;
&lt;br /&gt;
 如果你的工程成功被编译,JAR文件会生产在 &#039;&#039;target&#039;&#039; 目录下，具体在Eclipse的工作空间里名字为工程名的文件夹中。&lt;br /&gt;
 &lt;br /&gt;
 这个JAR文件就是一个能正常工作的Bukkit插件了。当然前提是你的plugin.yml里没出差错。&lt;br /&gt;
 &lt;br /&gt;
 你可以把这个jar文件丢到服务器的plugins文件夹里,重载或重启服务器,就可以测试你的新插件啦。&lt;br /&gt;
 &lt;br /&gt;
 为了连接上使用你自己的电脑开的服务器 ,在多人游戏的ip里填上你的本地ip即可。&lt;br /&gt;
 &lt;br /&gt;
 如果出现了错误而你又不能解决的话, 试试这里[http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], &lt;br /&gt;
 &lt;br /&gt;
 在这里提问[http://wiki.bukkit.org/IRC bukkitdev IRC channel],或者重新阅读wiki。&lt;br /&gt;
 &lt;br /&gt;
 当你做完了一个实用的插件,可以考虑把它发表在这里[http://dev.bukkit.org/ dev.bukkit]（为了Bukkit社区的访问量）。&lt;br /&gt;
 &lt;br /&gt;
 以上可知,JAR格式是默认压缩生成的(JAR文件基于ZIP文件的格式)。这么说来，在上传插件到Bukkit上时把JAR文件压缩成ZIP只会增加文件体积罢了。&lt;br /&gt;
 &lt;br /&gt;
 而且呢, 放在JAR中的config文件可以在检测不到插件目录（插件自动生成的文件夹，名字与插件名相同，里面是该插件的配置）下的config文件时自动生成。&lt;br /&gt;
 &lt;br /&gt;
 所以我们一般不把jar文件转换成其他格式。&lt;br /&gt;
&lt;br /&gt;
== 导入其它插件 ==&lt;br /&gt;
你可能会希望编辑其它那些有源代码的插件. 如果插件在它的目录下有&#039;&#039;pom.xml&#039;&#039;(大多数知名插件,例如WorldEdit和Essentials都有),你可以通过点击&#039;&#039;File -&amp;gt; Import&#039;&#039;来把它作为一个工程导入, 然后打开&#039;&#039;Maven&#039;&#039;项并选择&#039;&#039;Existing Maven Projects&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
[[Image:Importmaven.png]]&lt;br /&gt;
&lt;br /&gt;
然后选择&#039;&#039;pom.xml&#039;&#039;所在的目录,这个工程就应该出现在你的侧边栏里了。就像平常一样编辑并编译它吧。&lt;br /&gt;
&lt;br /&gt;
== 小贴士  ==&lt;br /&gt;
&lt;br /&gt;
=== 使玩家着火 ===&lt;br /&gt;
&lt;br /&gt;
 Bukkit API有很多碉堡的功能。以下是实现一些功能的代码片段。 &lt;br /&gt;
&lt;br /&gt;
下面的代码运行一个玩家使另一个玩家着火。通过类似于&#039;&#039;&#039;/ignite Notch&#039;&#039;&#039;这样的指令，这个指令会让 Notch 着火。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     // 使用 equalsIgnoreCase() 来忽视大小写&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;ignite&amp;quot;)) {&lt;br /&gt;
         // 确保玩家只输入了一个自变量(要被点着的玩家名).&lt;br /&gt;
         if (args.length != 1) {&lt;br /&gt;
             // 当return false时，该指令的使用信息会显示出来。&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 确保命令发出者是个玩家.&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can set other players on fire.&amp;quot;);&lt;br /&gt;
             sender.sendMessage(&amp;quot;This is an arbitrary requirement for demonstration purposes only.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 获取那个要被点着的玩家. 请注意下标起始是0，不是1.&lt;br /&gt;
         Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
 &lt;br /&gt;
         // 确保这个玩家在线。&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 让这个玩家着火1000 ticks (一秒钟大概有20ticks,所以总共是50秒).&lt;br /&gt;
         target.setFireTicks(1000);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 干掉玩家  ===&lt;br /&gt;
&lt;br /&gt;
 和标题一样.这里是要干掉某个玩家。 &lt;br /&gt;
&lt;br /&gt;
在你的onCommand方法里使用这个: &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;KillPlayer&amp;quot;)) {&lt;br /&gt;
         Player target = sender.getServer().getPlayer(args[0]);&lt;br /&gt;
          // 确保玩家在线.&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         target.setHealth(0); &lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这里有个小扩展,会使用一个爆炸来杀死玩家: &lt;br /&gt;
&lt;br /&gt;
 float explosionPower = 4F; //爆炸等级 - TNT的爆炸等级默认是4F&lt;br /&gt;
 Player target = sender.getWorld().getPlayer(args[0]);&lt;br /&gt;
 target.getWorld().createExplosion(target.getLocation(), explosionPower);&lt;br /&gt;
 target.setHealth(0);&lt;br /&gt;
&lt;br /&gt;
=== 创造一个假爆炸  ===&lt;br /&gt;
&lt;br /&gt;
这段代码会生产一个看起来和听起来与TNT/苦力怕产生的爆炸一样的效果。但是不会对实体和方块产生破坏。 在保留爆炸的艺术的同时又削弱了爆炸的威力，真是太有用了（&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onExplosionPrime(ExplosionPrimeEvent event) {	&lt;br /&gt;
     Entity entity = event.getEntity();&lt;br /&gt;
 &lt;br /&gt;
     // 如果事件和点燃的TNT有关,执行一些代码（又略）&lt;br /&gt;
     if (entity instanceof TNTPrimed) {&lt;br /&gt;
         entity.getWorld().createExplosion(entity.getLocation(), 0);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 使一个玩家对另外一个玩家隐形  ===&lt;br /&gt;
&lt;br /&gt;
 通过输入指令，能让指令执行者对特定的玩家隐形。 然而其他人仍然能看到指令执行者。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;HideMe&amp;quot;) &amp;amp;&amp;amp; args.length == 1) {&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can use this command!&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         // 检测指令执行者是玩家后，我们便可以安全的进行强制转换类操作。&lt;br /&gt;
        Player s = (Player) sender;&lt;br /&gt;
 &lt;br /&gt;
        // 获取命令执行者指定的玩家&lt;br /&gt;
        Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
        if (target == null) {&lt;br /&gt;
            sender.sendMessage(&amp;quot;Player &amp;quot; + args[0] + &amp;quot; is not online.&amp;quot;);&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        // 将 s 从 target 的视野中隐藏起来.&lt;br /&gt;
        target.hidePlayer(s);&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 在玩家准星所指处生产闪电 ===&lt;br /&gt;
&lt;br /&gt;
如下代码运行手持鱼竿的玩家通过点击来生成闪电 (当然要用准星瞄准)。 真是个简单好玩的恶作剧（。&lt;br /&gt;
&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onPlayerInteractBlock(PlayerInteractEvent event) {&lt;br /&gt;
     Player player = event.getPlayer();&lt;br /&gt;
     if (player.getItemInHand().getType() == Material.FISHING_ROD) {&lt;br /&gt;
         // 在给定坐标中生成一道闪电. 在本例中, 这个坐标是玩家准星瞄准的地方.&lt;br /&gt;
         // 只能指向200格以内的坐标.&lt;br /&gt;
         player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 自动格式 ===&lt;br /&gt;
&lt;br /&gt;
 Eclipse提供了将你的代码自动转成Oracle规定的格式, 修正了不符合常规的缩进, 空格等等。 在侧边栏选中你的项目,然后选择&#039;&#039;Source -&amp;gt; Format&#039;&#039;即可对其进行更改。&lt;br /&gt;
&lt;br /&gt;
== 插件需求专区  ==&lt;br /&gt;
&lt;br /&gt;
http://forums.bukkit.org/forums/plugin-requests.13/&lt;br /&gt;
&lt;br /&gt;
== 范例文件和样板  ==&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/wpeTPx7N Example.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have any more questions on this matter, don&#039;t hesitate to visit the BukkitDev [[IRC]] channel and ask!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:开发教程]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=1399</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=1399"/>
		<updated>2015-09-02T12:28:01Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​修整名称错误&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Configuration API Reference/en|点击此处浏览原文]]&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太OUT了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== 基本论述  ==&lt;br /&gt;
&lt;br /&gt;
=== Configuration对象 ===&lt;br /&gt;
&amp;lt;ref&amp;gt;key 键 value 值&amp;lt;/ref&amp;gt;&lt;br /&gt;
你的插件继承了 [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin], 由此,你也继承了JavaPlugin这个父类的的方法和属性. 继承的方法中, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; 返回的是一个 [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]类的对象.这个对象就代表你插件数据文件夹里的&#039;&#039;config.yml&#039;&#039; .&lt;br /&gt;
&lt;br /&gt;
插件初次使用&#039;&#039;getConfig()&#039;&#039;时会把&#039;&#039;config.yml&#039;&#039; 从磁盘中载入, 同时从jar中加载默认值. 但&#039;&#039;getConfig()&#039;&#039;的后续调用这时只会返回现存的&#039;&#039;FileConfiguration&#039;&#039; 对象. 除了显式保存，对此对象执行的操作不会写入到磁盘. 同样，在文件被加载之后的任何修改将不会被反映在对象中. 如果在你的数据文件夹中不存在 &#039;&#039;config.yml&#039;&#039; ,此时在程序中等价于存在一个空的&#039;&#039;config.yml&#039;&#039;, 也会加载一个空的&#039;&#039;FileConfiguration&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
{{warning| 如果你分配了利用&#039;&#039;getConfig()&#039;&#039;返回的对象，请不要将其分配给一个全局属性}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 若要如此，记住在重载配置文件之后再把 &#039;&#039;getConfig()&#039;&#039; 分配给一个变量}} &amp;lt;br /&amp;gt;&lt;br /&gt;
{{warning| 最好直接用&#039;&#039;getConfig()&#039;&#039;而不是将其分配给一个实例变量}}&lt;br /&gt;
&lt;br /&gt;
===  键值   ===&lt;br /&gt;
&lt;br /&gt;
配置文件由键值组合构成，所有的键都是字符串.值对于其他键来说可能是一个&#039;&#039;FileConfigurationSection&#039;&#039;或是独立的数据.  &#039;&#039;getKeys(boolean)&#039;&#039; 方法会返回现有&#039;&#039;FileConfigurationSection&#039;&#039;集合的值.布尔值决定返回的集合是不是递归的,如果为true，其将返回指定节点的键与子键, 如果为false则只会返回指定节点的键.为了获得特定节点的键, &#039;&#039;getKeys(boolean)&#039;&#039; 方法必须加载在特定节点. 你可以通过&#039;&#039;getConfigurationSection(String)&#039;&#039;方法来获取这个部分.&lt;br /&gt;
&lt;br /&gt;
{{warning| getKeys(boolean)方法返回的是这里的字符串[http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html 集合]}}&lt;br /&gt;
&lt;br /&gt;
=== 路径 ===&lt;br /&gt;
&lt;br /&gt;
Configuration API 使用路径来形成独立的键值组合.路径是键的集合用来将其与值关联的.每层都被路径分隔符区分开来,默认的分隔符是 &#039;.&#039; (段落).举例来说，下面的YAML文件就有着一个路径的集合.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== 默认值 ===&lt;br /&gt;
&lt;br /&gt;
你的jar中应该为用户准备一个默认的&#039;&#039;config.yml&#039;&#039;.当出现config.yml丢失或是不完整的情况时,程序将会访问jar内的 &#039;&#039;config.yml&#039;&#039;来获取值.准备好的文件必须被命名为 &#039;&#039;config.yml&#039;&#039; ，且应当放在与 [[Plugin YAML|plugin.yml]]相同的目录. The file should have the intended structure of your &#039;&#039;config.yml&#039;&#039;. This file can be copied as is into the datafolder by invoking saveDefaultConfig() on the Appropriate instance of JavaPlugin.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If dynamic key-value pairs are required as default values, they can added as defaults to the configuration with invocations of &#039;&#039;addDefault(String, Object)&#039;&#039; and &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; methods. &lt;br /&gt;
&lt;br /&gt;
In certain cases if you wish to append new key-value pairs to an existing config.yml you can set the copyDefaults to true for the ConfigurationOptions object. &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 创建一份config.yml的拷贝 ====&lt;br /&gt;
你可以使用JavaPlugin类中的saveDefaultConfig()方法，将jar中的config.yml拷贝一份至插件的数据文件夹中&amp;lt;ref&amp;gt;/plugins/YourPlugin/&amp;lt;/ref&amp;gt; . saveDefaultConfig()并不会覆盖已有的文件.&lt;br /&gt;
&lt;br /&gt;
=== 获得值 ===&lt;br /&gt;
&lt;br /&gt;
Reading values from the configuration involves invoking one of the many getter methods. A complete list of getters can be found [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html here]. Every getter method takes a configuration path detailed above. Some of the commonly used getter methods are as follows &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String) &lt;br /&gt;
*getInt(String) &lt;br /&gt;
*getString(String) &lt;br /&gt;
*getList(String) &lt;br /&gt;
*getStringList(String)&lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设定值 ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 保存文件 ===&lt;br /&gt;
&lt;br /&gt;
If make any changes to the&#039;&#039;FileConfiguration&#039;&#039; with set methods, or mutate any &#039;&#039;Lists&#039;&#039;, you will need to save the changes to disk if you wish to keep these changes after the plugin is disabled. To save the file to disk invoke the saveConfig method for your plugin, it will overwrite the file already there. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 从磁盘中重载配置文件 ===&lt;br /&gt;
&lt;br /&gt;
If you suspect that users have made changes to the &#039;&#039;config.yml&#039;&#039; in the data folder, those changes are not reflected in memory. Invoke the &#039;&#039;reloadConfig()&#039;&#039; method of your plugin to load from the disk again. It will destroy all changes in memory. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== Options  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== Arbitrary Configurations  ===&lt;br /&gt;
&lt;br /&gt;
If you require additional YAML files, for storing configuration information or persisting additional game information you will need to write your own methods for accessing the additional configuration files. Modeled after JavaPlugin getConfig, reloadConfig, saveConfig methods, the following is an example how to write your own methods to read and save to custom configuration files. Since these config files belong to your plugin, you can put this method in your main class so that you can have the same access as you have with config.yml. You will have to write a set of these methods for each YAML file. The advantage here, is that you can use each set in the same manner as the provided methods for the default config.yml. Alternately, adding additional methods can keep the method count lower and allow access to multiple files.&lt;br /&gt;
&lt;br /&gt;
==== Mirroring the JavaPlugin implementation ====&lt;br /&gt;
JavaPlugin implements methods for config.yml. A plugin needs to implement its own methods to access configuration files unique to the plugin. After implementing the methods for the plugin, they can be invoked in the same context as the inherited &#039;&#039;getConfig()&#039;&#039;, &#039;&#039;reloadConfig()&#039;&#039;, &#039;&#039;saveConfig()&#039;&#039;, and &#039;&#039;saveDefaultConfig()&#039;&#039; methods from JavaPlugin. The following can be made into a single class which allows access to any yaml file. Such a class can be found [https://gist.github.com/3174347 here]&lt;br /&gt;
&lt;br /&gt;
First you will need to declare two fields and initialize them to null for each of the custom configuration files. One to hold the &#039;&#039;FileConfiguration&#039;&#039; object and one to hold the &#039;&#039;File&#039;&#039; object. The File object represents the file on the disk, and the FileConfiguration represents the contents of the configuration. &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Reloading =====&lt;br /&gt;
Then, write the method that is responsible for loading the config from disk. It will load the file, and search the jar for a default customConfig.yml. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Getting =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Saving =====&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Defaults =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Example Use  ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules)&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1365</id>
		<title>Bukkit/插件开发教程</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1365"/>
		<updated>2015-09-01T12:28:23Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​翻译了控制物品の附魔属性&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
{{模板:待完善}}&lt;br /&gt;
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]&lt;br /&gt;
== 介绍  ==&lt;br /&gt;
&lt;br /&gt;
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.&lt;br /&gt;
&lt;br /&gt;
== 学习Java  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;理解这些教程需要具备Java这门编程语言的基本知识.&#039;&#039;&#039;如果你刚开始学习Java或者需要重温一下相关知识，下面有一个并不完整的网站列表可供参考.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;[http://docs.oracle.com/javase/tutorial/ Oracle&#039;s Java Tutorials]&amp;lt;/big&amp;gt; - 官方教程&lt;br /&gt;
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程&lt;br /&gt;
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程&lt;br /&gt;
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书&lt;br /&gt;
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.&lt;br /&gt;
&lt;br /&gt;
== 开发环境  ==&lt;br /&gt;
&lt;br /&gt;
在编写一个插件前 (或学习Java前) 你需要配置一个开发者环境，这个包括但不限于安装IDE (Integrated Development Environment，集成开发环境)。接下来的教程中有关于Eclipse上IDE的操作指南。&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;更多信息，请参见 [[Bukkit/插件开发教程-建立工作区|建立工作区]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
你&#039;&#039;&#039;必须&#039;&#039;&#039;下载&#039;&#039;&#039;Java&#039;&#039;&#039;开发者使用的Eclipse构建版本, &#039;&#039;&#039;不是&#039;&#039;&#039; &#039;&#039;&#039;Java EE&#039;&#039;&#039;开发者的版本。 Java EE 开发者的版本不包含此教程需要的Maven支持。&lt;br /&gt;
&lt;br /&gt;
== 新建插件开发项目  ==&lt;br /&gt;
=== 创建一个新的项目 ===&lt;br /&gt;
&lt;br /&gt;
在开始工作之前，你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击&#039;&#039;File -&amp;gt; New -&amp;gt; Project:&#039;&#039;来创建一个新的项目.&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject.png]] &lt;br /&gt;
&lt;br /&gt;
现在,打开&#039;&#039;Maven&#039;&#039;文件夹, 然后选择&#039;&#039;Maven Project&#039;&#039;.点击next,之后在下一个菜单中选择&#039;&#039;Create a simple project&#039;&#039;, 再次点击&#039;&#039;Next&#039;&#039;:&lt;br /&gt;
如果你看不到&#039;&#039;Maven&#039;&#039;文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject2.png]] &lt;br /&gt;
&lt;br /&gt;
现在，你需要给你的组用户命名，就像下面这样:&lt;br /&gt;
* 如果你拥有一个域名，package则填写逆转的域名地址. &lt;br /&gt;
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]&lt;br /&gt;
** 避免使用一个不属于你自己的域名.&lt;br /&gt;
* 没有域名? 这里有几种方法可供选择。&lt;br /&gt;
*# 在资源管理站点创建一个用户，比如GitHub或是sourceforge&lt;br /&gt;
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 使用你的邮箱.  例如: &amp;lt;username&amp;gt;@gmail.com格式的邮箱应输入com.gmail.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 最不济的方法.：  使用独特的组名命名方式，这应当是你最后的解决方法。&lt;br /&gt;
&lt;br /&gt;
以下几个地址不能作为package中的地址前缀:&lt;br /&gt;
* org.bukkit&lt;br /&gt;
* net.bukkit&lt;br /&gt;
* com.bukkit&lt;br /&gt;
* net.minecraft&lt;br /&gt;
&lt;br /&gt;
完成基础的组名以后，你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 &#039;&#039;TestPlugin&#039;&#039;的插件，那么完整的组名是&#039;&#039;io.github.&amp;lt;username&amp;gt;&#039;&#039;, 你的工程名也是 &#039;&#039;TestPlugin&#039;&#039;. 至于版本，默认即可，稍后可以修改。&lt;br /&gt;
完成向导:&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject3.png]] &lt;br /&gt;
&lt;br /&gt;
如果这是你首次使用Eclipse, 点击右上角的&amp;quot;X&amp;quot; 来关闭Welcome提示页面. 现在，你的窗口视图看起来就像下面的图片这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Eclipsemain.png]] &lt;br /&gt;
&lt;br /&gt;
点击工程名称右边的箭头来进行下一步，现在我们正式开始。&lt;br /&gt;
=== 添加Bukkit API ===&lt;br /&gt;
&lt;br /&gt;
在开发插件之前，你需要添加 Bukkit API库文件到你的项目，作为一个dependency, 你也可以添加其他你想实用的API.&lt;br /&gt;
&lt;br /&gt;
找到项目文件夹中部的&#039;&#039;pom.xml&#039;&#039;并双击进行编辑. 点击&#039;&#039;pom.xml&#039;&#039;中部的 tab,你将会看到下图所示内容:&lt;br /&gt;
&lt;br /&gt;
[[Image:Pomeditor.png]]&lt;br /&gt;
&lt;br /&gt;
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;build&amp;gt;&lt;br /&gt;
       &amp;lt;plugins&amp;gt;&lt;br /&gt;
           &amp;lt;plugin&amp;gt;&lt;br /&gt;
               &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;
               &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
               &amp;lt;configuration&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;amp;lt;&amp;lt;/nowiki&amp;gt;source&amp;gt;1.7&amp;lt;/source&amp;gt;&lt;br /&gt;
                   &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;&lt;br /&gt;
               &amp;lt;/configuration&amp;gt;&lt;br /&gt;
           &amp;lt;/plugin&amp;gt;&lt;br /&gt;
       &amp;lt;/plugins&amp;gt;&lt;br /&gt;
    &amp;lt;/build&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你或许想使用其他版本,例如 &amp;lt;code&amp;gt;1.8&amp;lt;/code&amp;gt; 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码&amp;quot;error&amp;quot;中建议你更改语言版本.同意即可.&lt;br /&gt;
&lt;br /&gt;
在位于代码中段的&#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit&#039;s repository的地址): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;repositories&amp;gt;&lt;br /&gt;
        &amp;lt;repository&amp;gt;&lt;br /&gt;
            &amp;lt;id&amp;gt;bukkit-repo&amp;lt;/id&amp;gt;&lt;br /&gt;
            &amp;lt;url&amp;gt;http://repo.bukkit.org/content/groups/public/&amp;lt;/url&amp;gt;&lt;br /&gt;
        &amp;lt;/repository&amp;gt;&lt;br /&gt;
    &amp;lt;/repositories&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后继续在 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;dependencies&amp;gt;&lt;br /&gt;
        &amp;lt;dependency&amp;gt;&lt;br /&gt;
            &amp;lt;groupId&amp;gt;org.bukkit&amp;lt;/groupId&amp;gt;&lt;br /&gt;
            &amp;lt;artifactId&amp;gt;bukkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
            &amp;lt;version&amp;gt;1.7.2-R0.3&amp;lt;/version&amp;gt;&lt;br /&gt;
            &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;
            &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;
        &amp;lt;/dependency&amp;gt;&lt;br /&gt;
    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.&lt;br /&gt;
&lt;br /&gt;
当你完成上述步骤后,&#039;&#039;pom.xml&#039;&#039;中的内容应该是这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Finishedpom.png]]&lt;br /&gt;
&lt;br /&gt;
通过菜单 &#039;&#039;File -&amp;gt; Save&#039;&#039;或者按住 &amp;lt;code&amp;gt;Ctrl + S&amp;lt;/code&amp;gt; 来保存&#039;&#039;pom.xml&#039;&#039;文件 .  之后, 右键项目标题，然后依次选择 &#039;&#039;Maven -&amp;gt; Update Project&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Bukkit Java文档 ===&lt;br /&gt;
&lt;br /&gt;
 如果你曾经使用过Eclipse和Java，你会知道当你将鼠标准心移至class或者 method中的代码部分时，一个含有相关文档内容的黄色小框会弹出来. 这就是（Java的）Java文档。&lt;br /&gt;
 &lt;br /&gt;
 你可以[http://download.oracle.com/javase/6/docs/api/ Oracle website 在线获取]它.。Bukkit同样拥有文档，这些文档内容包含了关于API中method和class的有用描述。&lt;br /&gt;
 &lt;br /&gt;
 你可以在[http://jd.bukkit.org/ 这]找到它 (Beta版的Java文档在[http://jd.bukkit.org/ 这]找, 新开发出来的Java文档在  &lt;br /&gt;
 &lt;br /&gt;
 [http://jd.bukkit.org/ 这]) 。为了在Eclipse里使用Java文档,这样的话当你的鼠标停留在Bukkit的class和method上时，&lt;br /&gt;
 &lt;br /&gt;
 （Bukkit的）Java文档就会冒出来。  &lt;br /&gt;
JavaDoc in spigot [https://hub.spigotmc.org/javadocs/bukkit/ Click Here]&lt;br /&gt;
&lt;br /&gt;
 首先右键在侧边栏里的&amp;quot;Maven Dependencies&amp;quot;下面的Bukkit jar,选择&amp;quot;Properties&amp;quot;.选中窗口左边的&#039;&#039;Javadoc Location&#039;&#039;, 并粘贴如下网址 &lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;http://jd.bukkit.org/&#039;&#039;&#039; (或者上面的测试版/最新版 Java文档链接也好) 到&amp;quot;Javadoc URL&amp;quot;下面的输入框内. 就像这样: &lt;br /&gt;
&lt;br /&gt;
[[Image:Bukkitjavadocs.png]] &lt;br /&gt;
&lt;br /&gt;
 点击 &amp;quot;Validate&amp;quot;,然后再点击&amp;quot;OK&amp;quot;就完成了.现在Bukkit Javadocs已经连接到Bukkit提供的资源,同时你也可以通过Eclipse来获得帮助类文档信息了.&lt;br /&gt;
&lt;br /&gt;
=== 创建一个包 ===&lt;br /&gt;
&lt;br /&gt;
 现在你需要创建一个包，它将储存所有我们需要使用的Java类文件. 右键展开&#039;&#039;src/main/java&#039;&#039;折叠并选择 &#039;&#039;New &amp;amp;gt; Package&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
[[Image:Newpackage.png]] &lt;br /&gt;
&lt;br /&gt;
 包名的话，先写上你的组名，然后加个点，再写上你的小写Artifact ID。举例, 如果你的组名是&#039;&#039;io.github.name&#039;&#039;，你的artifact名是 &#039;&#039;TestPlugin&#039;&#039;。&lt;br /&gt;
 &lt;br /&gt;
 你的包名就应该是&#039;&#039;io.github.name.testplugin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== 创建插件的类文件 ===&lt;br /&gt;
&lt;br /&gt;
既然我们已经建立好了我们的项目，我们接下来可以开始添加类文件以及制作插件了. 插件的主类指的是拓展(extends)JavaPlugin的类文件. 在你的插件中只能有一个类文件拓展JavaPlugin，无论直接还是间接. 先创建你的主类文件并保持名称与插件名一致是个很好的习惯.右键你之前新建的包,并选择&amp;amp;nbsp; &#039;&#039;New &amp;amp;gt; Class&#039;&#039;.你将会创建一个新的类文件，就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$GroupName}.{$ArtifactName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$ArtifactName} extends JavaPlugin {&lt;br /&gt;
    &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
警告： 插件绝对不应该调用自己的构造函数并实例化&lt;br /&gt;
&lt;br /&gt;
=== 创建plugin.yml ===&lt;br /&gt;
&lt;br /&gt;
你已经创建了项目和主类，如果你想要Bukkit能够加载你的插件，你还必须创建 &#039;&#039;&#039;[[Plugin YAML|plugin.yml]]&#039;&#039;&#039; 文件. 这个文件包含有基础的插件信息，如果缺失这个文件，你的插件也将会失效. 此时你需要右键&#039;&#039;src/main/resources&#039;&#039;. 选择&#039;&#039;New &amp;amp;gt; File&#039;&#039;. 命名为&amp;quot;&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;&amp;quot; 并右键完成新文件的创建.Eclipse会打开默认的文本编辑窗口来显示&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;文件中空白的内容并提供编辑. &#039;&#039;(Hint:&amp;amp;nbsp;如果你想要使得你的工作空间更加规整，关闭文本编辑器并将&#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件拖到主工作空间(拖到文件目录的右边) 之后你就可以在eclipse中编辑文件了.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
有三个基础的内容需要在plugin.yml写明. &lt;br /&gt;
: name:  插件名称. &lt;br /&gt;
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 插件主类的完整，合法名称] . &lt;br /&gt;
: version: 插件当前版本号. &lt;br /&gt;
&lt;br /&gt;
最简单的 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件内就像这样&amp;amp;nbsp;: &lt;br /&gt;
&lt;br /&gt;
 name: {插件名称}&lt;br /&gt;
 main: {包名}.{主类}&lt;br /&gt;
 version: {版本号}&lt;br /&gt;
&lt;br /&gt;
 PS:插件的包名经常会包括插件的名称，所以看到这个的时候不要感到惊讶。&lt;br /&gt;
    你的主类是否是你的插件名取决于你之前的命名方式，时刻记住这一点很重要。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;更多例子&#039;&#039;&#039;, 请看[[#Example_Files_and_Templates]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
此时你的插件已经可以被Bukkit加载了,同时服务端日志也会开始记录你的插件. 不过它现在什么用都没有!&lt;br /&gt;
&lt;br /&gt;
== onEnable()和onDisable()方法  ==&lt;br /&gt;
&lt;br /&gt;
这些方法将在插件启用与卸载时生效. 默认情况下，你的插件在被加载时会调用这些方法，所以你可以在这里注册你需要用到的事件和提供一些调试信息. &amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 方法会在插件启用时被调用, 需要包含初始化插件的逻辑语句. &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法会在插件卸载时被调用，需要包含清理(clean up)插件的逻辑语句和连接声明(associated state)。此外，插件可以重写 &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; 方法来在插件载入时执行附加的逻辑语句。&lt;br /&gt;
&lt;br /&gt;
=== onEnable()和onDisable()方法介绍  ===&lt;br /&gt;
&lt;br /&gt;
在前面的章节中，我们在主类创建了&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法. 此时，这些代码看起来就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
     @Override&lt;br /&gt;
     public void onEnable() {&lt;br /&gt;
         // 插件载入时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     @Override&lt;br /&gt;
     public void onDisable() {&lt;br /&gt;
         // 插件卸载时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这些方法已经创建，但目前它们还没有任何作用.&lt;br /&gt;
注意: 不需要添加代码来实现输出信息&amp;quot;{$PluginName} has been enabled!&amp;quot; ，因为bukkit会自动输出此类信息&lt;br /&gt;
有关更多事件的信息请查看 [[Event_API_Reference|这里]].&lt;br /&gt;
&lt;br /&gt;
=== 发送提示信息 ===&lt;br /&gt;
&lt;br /&gt;
 插件能够输出信息至控制台与服务器日志。这是通过调用插件记录器里正确的方法来实现的。首先我们必须调用 &amp;lt;code&amp;gt;getLogger()&amp;lt;/code&amp;gt; 方法来检索该插件的记录器，&lt;br /&gt;
 &lt;br /&gt;
 然后我们就可以开始记录信息了。&lt;br /&gt;
&lt;br /&gt;
我们通过调用&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法可以实现输出信息。 我们需要在&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法下面插入如下代码：&lt;br /&gt;
&lt;br /&gt;
 getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
在 &amp;lt;code&amp;gt;&#039;&#039;&#039;onDisable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法里面，你也可以做类似的事情，不过要改改输出信息。&lt;br /&gt;
&lt;br /&gt;
此时你的主类文件就像这样: &lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
         @Override&lt;br /&gt;
         public void onEnable() {&lt;br /&gt;
             getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public void onDisable() {&lt;br /&gt;
         getLogger().info(&amp;quot;onDisable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 防止重载后插件失效 ===&lt;br /&gt;
&lt;br /&gt;
你需要知道的是，插件并不只会伴随服务器关闭、启动而重载，你的插件也可能会被其他插件启用、关闭，或是在服务器运行时使用/reload命令. &lt;br /&gt;
假设服务器启动之后一会儿后，你的插件才被启用，这时便是危险的,因为服务器中可能已经有玩家在线、额外的世界被加载以及很多预想不到的不同之处(与&amp;quot;插件同服务器同时加载&amp;quot;的情况相比较). &lt;br /&gt;
&lt;br /&gt;
举个例子来说:&lt;br /&gt;
* 你有一个用来储存HashMap中的玩家登入信息的插件&lt;br /&gt;
* 你希望该信息对所有玩家都可见&lt;br /&gt;
* 一名管理员使用/reload命令&lt;br /&gt;
* 你的插件被卸载，所有储存的数据都丢失了&lt;br /&gt;
* 你的插件被再次启用时，一些玩家已经在线&lt;br /&gt;
* 这些玩家在HashMap并没有储存的信息&lt;br /&gt;
* 你试图重新获得他们的信息，但是失败了!&lt;br /&gt;
&lt;br /&gt;
为了插件重载以后能够正常工作, 你需要在插件启用时加载所有已经在线的玩家的信息并将之储存在HashMap中.&lt;br /&gt;
&lt;br /&gt;
 for (Player player : Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
     playerList.put(player.getName(), playerData(player));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== 监听器  ==&lt;br /&gt;
监听器是一种方法被调用来对事件作出反应的类.所有的监听器使用 org.bukkit.event.Listener接口.更多有关监听器创建的细节,&lt;br /&gt;
:: &#039;&#039; 请查看: [[Event API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 指令  ==&lt;br /&gt;
&lt;br /&gt;
=== onCommand() 方法  ===&lt;br /&gt;
&lt;br /&gt;
 现在你已经知道如何注册一个事件并做出响应,但是如果你只是想要在命令输入之后做出响应呢? 你可以使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 这个代码的作用是当玩家输入“/”时，监听该操作并执行相关语句&amp;quot;/&amp;quot; . 举个例子来说.输入 &amp;quot;/do something&amp;quot; 将会执行 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 此种情况下，因为没有特定的行为被编程，所以并不会发生任何事情。&lt;br /&gt;
&lt;br /&gt;
 请避免使用和bukkit所提供的指令重名的指令, 然后深思你的指令名的唯一性。 &lt;br /&gt;
 &lt;br /&gt;
 例如.指令&amp;quot;give&amp;quot;已经被好几个插件使用了, 如果你执行了另外一个&amp;quot;give&amp;quot;指令, 你的插件将会和这些插件冲突。&lt;br /&gt;
&lt;br /&gt;
 你必须在插件的&amp;quot;plugins.yml&amp;quot;注册你的指令 否则这个方法将不会被触发。&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法必须返回一个布尔值（true或false）。 &lt;br /&gt;
 &lt;br /&gt;
 如果返回值是true，你不会看到什么明显的事情发生。 &lt;br /&gt;
 &lt;br /&gt;
 但如果返回值是false，则会返回你的plugin.yml里的&#039;usage:property&#039;然后发送给命令使用者. &lt;br /&gt;
&lt;br /&gt;
当使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时, 你需要填写4个参数. &lt;br /&gt;
&lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 发送命令的对象&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;Command cmd&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行的指令&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String commandLabel&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行指令的别名&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String[] args&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 该指令的自变量数组。&lt;br /&gt;
 &lt;br /&gt;
例如.指令 &#039;&#039;/hello abc def&#039;&#039; 中， &#039;&#039;abc&#039;&#039; 会被存放进args[0]中, &#039;&#039;def&#039;&#039; 被存放进args[1]中。&lt;br /&gt;
&lt;br /&gt;
==== 设置命令  ====&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { // 如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
     // 所需要执行的事（此处略）&lt;br /&gt;
         return true;&lt;br /&gt;
     } //如果以上内容成功执行，则返回true &lt;br /&gt;
     // 如果执行失败，则返回false.&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 每当使用&amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时， 在每个方法后面都加上一个 return false; 是一个惯例。 &lt;br /&gt;
 &lt;br /&gt;
 返回false会显示在plugin.yml里设置的usage信息 (看下面). 用这种方法的话，一旦执行出错便会显示usage信息。&lt;br /&gt;
&lt;br /&gt;
 每当方法return一个值的时候，这个方法就会退出，所以return true的时候，在它下面的代码就会被跳过, &lt;br /&gt;
 &lt;br /&gt;
 除非return语句在一个if的嵌套中，或者类似的嵌套情况。 &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;.equalsIgnoreCase(&amp;quot;basic&amp;quot;)&#039;&#039;&#039;&amp;lt;/code&amp;gt;代表忽略英文大小写. 在这种情况下,字符串&amp;quot;BAsIc&amp;quot; 和 &amp;quot;BasiC&amp;quot;和 basic相同，代码会被照常执行。&lt;br /&gt;
&lt;br /&gt;
和以前一样，加两行import在你的java文件头&lt;br /&gt;
&lt;br /&gt;
 import org.bukkit.command.Command;&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.command.CommandSender;&lt;br /&gt;
&lt;br /&gt;
=== 在Plugin.yml中添加你的指令  ===&lt;br /&gt;
&lt;br /&gt;
你需要添加你的指令到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件里. 如下是指令/basic添加到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039;的例子（请在plugins.yml的末尾添加如下代码）: &lt;br /&gt;
&lt;br /&gt;
commands:&lt;br /&gt;
   basic:&lt;br /&gt;
      description: This is a demo command.&lt;br /&gt;
      usage: /&amp;lt;command&amp;gt; [player]&lt;br /&gt;
      permission: &amp;lt;plugin name&amp;gt;.basic&lt;br /&gt;
      permission-message: You don&#039;t have &amp;lt;permission&amp;gt;&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;basic&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令名称&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;description&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令描述&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;usage&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 在oncommand方法return false后显示的用法提示。尽量简洁, 使别人能够理解指令是什么以及如何使用它。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被一些显示帮助信息的插件使用，用来决定向玩家显示哪个指令（完全不对劲吧！）。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission-message&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 当玩家使用了这个指令而没有权限时输出的信息。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 PS:yml文件使用两个空格作为制表符,使用tab键输入制表符会导致错误。&lt;br /&gt;
&lt;br /&gt;
===控制台指令vs玩家指令===&lt;br /&gt;
&lt;br /&gt;
 你可能注意到了上文的&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt;参数. &amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt; 是个Bukkit接口，&lt;br /&gt;
 &lt;br /&gt;
 它有两个（对插件编写者）有用的子类：&amp;lt;code&amp;gt;&#039;&#039;&#039;Player&#039;&#039;&#039;&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;&#039;&#039;&#039;ConsoleCommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 所以当你编写插件的时候, 确定这个指令从控制台发出后能完全正常工作是十分必要的,&lt;br /&gt;
 &lt;br /&gt;
 那些只能由在线玩家执行的指令 &#039;&#039;只能&#039;&#039; 由一个在线玩家执行。&lt;br /&gt;
 &lt;br /&gt;
 有的插件在判断命令执 行对象不是玩家时仅仅用return进行处理 (比如当有人从控制台发出指令), &lt;br /&gt;
 &lt;br /&gt;
 即便这些指令能够在控制台完美运行（比如改变天气的指令）。&lt;br /&gt;
&lt;br /&gt;
一个判断sender的方法: &lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { //如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
             // 所需要执行的事（此处略）&lt;br /&gt;
             return true;&lt;br /&gt;
         } else if (cmd.getName().equalsIgnoreCase(&amp;quot;basic2&amp;quot;)) {&lt;br /&gt;
             if (!(sender instanceof Player)) { //如果sender与Player类不匹配&lt;br /&gt;
                 sender.sendMessage(&amp;quot;This command can only be run by a player.&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 Player player = (Player) sender;&lt;br /&gt;
                    // 所需要执行的事（此处略）&lt;br /&gt;
             }&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 在这个例子里，指令&#039;&#039;&#039;basic&#039;&#039;&#039;能由任何人发出 - 一个在线的玩家, 或者服务器后台的op. &lt;br /&gt;
 &lt;br /&gt;
 但是指令&#039;&#039;&#039;basic2&#039;&#039;&#039;只能由一个在线的玩家发出. &lt;br /&gt;
&lt;br /&gt;
 大体上说,你应该允许尽可能多的指令在控制台和玩家聊天框里都正常执行. &lt;br /&gt;
 &lt;br /&gt;
 需要在线玩家执行的指令可以使用以上例子中的机制来检测&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;是一个玩家. &lt;br /&gt;
 &lt;br /&gt;
 很多指令广泛依靠于一些玩家的属性（例如可以被tp或被给予物品等等）。&lt;br /&gt;
&lt;br /&gt;
 如果你想更进一步, 你可以对你的指令的自变量做一些额外的检测， 例如传送指令在提供玩家ID的情况下（也只有在这种情况下）可以在控制台被执行。&lt;br /&gt;
&lt;br /&gt;
=== 使用独立的 CommandExecutor class  ===&lt;br /&gt;
&lt;br /&gt;
 以上的例子必须把 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法放在插件的主类里。&lt;br /&gt;
 &lt;br /&gt;
 对于小插件来说，这是极好的,但如果你要写一个大点的插件,把你的 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法放在独立的类里将会很有意义。&lt;br /&gt;
 &lt;br /&gt;
 幸运的是，这并不难: &lt;br /&gt;
&lt;br /&gt;
*在你的包里创建一个新类. 命名为&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;之类的 (当然，要把&#039;&#039;&#039;MyPlugin&#039;&#039;&#039;替换成你制作的插件的名字)。 这个类 &#039;&#039;&#039;必须&#039;&#039;&#039; 继承Bukkit 的&#039;&#039;&#039;CommandExecutor&#039;&#039;&#039;接口。&lt;br /&gt;
&lt;br /&gt;
*在你的插件的&amp;lt;code&amp;gt;&#039;&#039;&#039;onEnable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法中,你需要实例化你创建的命令执行类(CommandExecutor class), 然后做一个像下面一样的调用：&amp;lt;code&amp;gt;getCommand(&amp;quot;basic&amp;quot;).setExecutor(myExecutor);&amp;lt;/code&amp;gt;&lt;br /&gt;
 其中&amp;quot;basic&amp;quot;是我们需要操控的指令, &amp;lt;code&amp;gt;myExecutor&amp;lt;/code&amp;gt; 是我们创建的实例.&lt;br /&gt;
&lt;br /&gt;
废话少说直接吔我例子: &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyPlugin.java&#039;&#039;&#039; (插件的主类): &lt;br /&gt;
 @Override&lt;br /&gt;
 public void onEnable() {&lt;br /&gt;
         // 如果你没有在plugins.yml注册过指令的话，此处会抛出空指针异常！&lt;br /&gt;
         this.getCommand(&amp;quot;basic&amp;quot;).setExecutor(new MyPluginCommandExecutor(this));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;MyPluginCommandExecutor.java&#039;&#039;&#039;（独立CommandExecutor类）: &lt;br /&gt;
 public class MyPluginCommandExecutor implements CommandExecutor {&lt;br /&gt;
         private final MyPlugin plugin;&lt;br /&gt;
 &lt;br /&gt;
         public MyPluginCommandExecutor(MyPlugin plugin) {&lt;br /&gt;
             this.plugin = plugin; // Store the plugin in situations where you need it.&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
             // 和以前一样执行就好…&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 请注意我们是如何把主类里的对象指向&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;的. 这允许我们轻易的使用主类对象的方法（如果我们需要的话）。&lt;br /&gt;
&lt;br /&gt;
 这么做的话, 我们便能更好的组织代码 - 如果 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法又大又复杂, 它就能被划分为子类，从而不使插件的主类显得凌乱。&lt;br /&gt;
&lt;br /&gt;
 PS：如果你的插件含有多个指令的话, 你需要为每一个指令单独设立一个commandexecutor.&lt;br /&gt;
&lt;br /&gt;
=== 写一个安全的指令 ===&lt;br /&gt;
 当写一个指令的时候,别进行任何假设（很重要）, 例如假设执行者一定是个玩家. 请牢记以下原则:&lt;br /&gt;
&lt;br /&gt;
==== 在命令执行前确定发出者是玩家 ====&lt;br /&gt;
&lt;br /&gt;
使用简单的例子来进行检测:&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (sender instanceof Player) {&lt;br /&gt;
             Player player = (Player) sender;&lt;br /&gt;
             // do something&lt;br /&gt;
         } else {&lt;br /&gt;
             sender.sendMessage(&amp;quot;You must be a player!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
             // do something&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 检测自变量个数 ====&lt;br /&gt;
&lt;br /&gt;
 别老是认为玩家都能打对正确数量的自变量. &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (args.length &amp;gt; 4) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Too many arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         } &lt;br /&gt;
         if (args.length &amp;lt; 2) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Not enough arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 在获取玩家之前先检测他们是否在线 ====&lt;br /&gt;
&lt;br /&gt;
 有时你想通过命令发出者打出的ID来获取其他玩家，请确保他们在线再说！&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         Player target = (Bukkit.getServer().getPlayer(args[0]));&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not online!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 如果你想对离线玩家进行操作，&amp;lt;code&amp;gt;&#039;&#039;&#039;OfflinePlayer&#039;&#039;&#039;&amp;lt;/code&amp;gt;类提供了基础操作的方法.&lt;br /&gt;
&lt;br /&gt;
== 插件的配置/设置  ==&lt;br /&gt;
Bukkit API 为插件提供了一种简便的方法，用来管理用户自定义的设置。同时，通过这种方法也可以储存数据。&lt;br /&gt;
:: &#039;&#039;详情请查看: [[Configuration API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 权限  ==&lt;br /&gt;
&lt;br /&gt;
利用新的Bukkit API来设置权限是再容易不过的事情. 检测玩家是否拥有权限，可以使用以下代码: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;if (player.hasPermission(&amp;quot;some.pointless.permission&amp;quot;)) {&lt;br /&gt;
   //执行此段代码(有权限)&lt;br /&gt;
} else {&lt;br /&gt;
   //执行此段代码(无权限)&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你也可以检测一个权限节点是否被定义过，(等效于 Java 语言中的&#039;&#039;&#039;null&#039;&#039;&#039;)，使用下面的方法: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;boolean isPermissionSet(String name)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你或许对此抱有疑惑:为什么这段代码中没有任何用户群的信息？因为它们并不是必需的. 之前这段代码想要实现的主要功能之一就是根据用户群格式化聊天信息. 但其实使用权限就可以很容易地完成这项工作.你可以在聊天插件的配置文件中定义权限与前缀的对应关系. 举例来说，&amp;quot;someChat.prefix.admin&amp;quot; 权限可以对应前缀[Admin].当拥有此权限的玩家发出聊天信息时 ，聊天前缀便是[Admin]. &lt;br /&gt;
&lt;br /&gt;
另一个想要实现的主要功能则是给所有拥有权限的在线用户发一条信息.你可以这样做: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;for (Player player: Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
    if (player.hasPermission(&amp;quot;send.receive.message&amp;quot;)) {&lt;br /&gt;
        player.sendMessage(&amp;quot;You were sent a message&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
看到这里你可能会问，若是没有权限组我该如何配置玩家的 [[权限]] ？既然 [[Bukkit]] API 本身并没有提供用户组的预设, 你必须通过安装一款权限组插件，像 [[permissionsBukkit]]  这样的插件来帮助你配置用户群. &#039;&#039;&#039;API 只是提供接口而不是提供implementation.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
=== 配置权限 ===&lt;br /&gt;
{{Hide|标题=译者注 |内容= 其一，以下代码中部分内容将以&#039;&#039;//文字&#039;&#039;翻译，使用时请不要加入其中内容，仅做教程中的翻译参考；&lt;br /&gt;
其二，default:true在下面翻译为全员默认拥有该权限，default:false则翻译为全员默认没有该权限，请注意理解。 }}&lt;br /&gt;
如果你想要更多地控制你的权限，例如设置权限的默认值或是子权限节点，不如将权限节点加入你的 &#039;&#039;[[plugin.yml]]&#039;&#039;文件.这并不是必须的，但我们推荐你这样做. 下面是一个实例，显示了当你将权限添加在&#039;&#039;plugin.yml&#039;&#039;文件末尾时的样子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands //给予使用doorman所有命令的权限&lt;br /&gt;
        children:  //子权限节点&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&lt;br /&gt;
    doorman.kick:&lt;br /&gt;
        description: Allows you to kick a user  //允许你踢出一名用户&lt;br /&gt;
        default: op  //OP默认拥有该权限&lt;br /&gt;
    doorman.ban:&lt;br /&gt;
        description: Allows you to ban a user  //允许你封禁一名用户&lt;br /&gt;
        default: op //OP默认拥有该权限&lt;br /&gt;
    doorman.knock:&lt;br /&gt;
        description: Knocks on the door! //敲门&lt;br /&gt;
        default: true //全员默认拥有该权限&lt;br /&gt;
    doorman.denied:&lt;br /&gt;
        description: Prevents this user from entering the door  //禁止拥有该权限节点的用户进入门内&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
值得注意的是,你的插件使用的每个权限都被定义为 &#039;&#039;permissions&#039;&#039; 这个节点的子权限节点. 每个权限都能够拥有一个描述，默认值以及子权限节点。&lt;br /&gt;
==== 默认值  ====&lt;br /&gt;
&lt;br /&gt;
当一个权限没有明确是否为玩家所拥有&amp;amp;nbsp;，&#039;&#039;hasPermission&#039;&#039;&amp;lt;ref&amp;gt;这是一个检测玩家是否拥有指定权限节点的方法&amp;lt;/ref&amp;gt;&amp;amp;nbsp;将返回false&amp;lt;ref&amp;gt;该方法返回的boolean值&amp;lt;/ref&amp;gt;. 在你的[[plugin.yml]] you can change this by setting the default node to be one of four values: &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;true&#039;&#039;&#039; - 全员默认拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;false&#039;&#039;&#039; - 全员默认没有该权限. &lt;br /&gt;
*&#039;&#039;&#039;op&#039;&#039;&#039; - 如果玩家是一名OP&amp;lt;ref&amp;gt;Operator，服务器管理员&amp;lt;/ref&amp;gt;，那么他将拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;not op&#039;&#039;&#039; - 如果玩家不是一名OP，那么他将拥有该权限.&lt;br /&gt;
&lt;br /&gt;
==== 子权限  ====&lt;br /&gt;
&lt;br /&gt;
在这之前你可能已经习惯了通过权限节点自动分配所有的子权限&amp;lt;ref&amp;gt;原句:Before now you will probably be used to the * permission to automatically assign all sub permissions.此处的sub permissions应是同上文“你的插件使用的每个权限都被定义为 permissions 这个节点的子权限节点.”相照应&amp;lt;/ref&amp;gt;. 现在这种方法已经随着bukkit API&amp;lt;ref&amp;gt;应用程序编程接口(Application Programming Interface)&amp;lt;/ref&amp;gt;的更新而改变，现在你可以自己定义子权限了. 这将使得插件具有更多的灵活性. 下面是一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands  //翻译见上&lt;br /&gt;
        children:&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
在这里，&#039;&#039;doorman.*&#039;&#039; 权限拥有几个子权限并分配在其下. 子权限工作的方式就是当&#039;&#039;doorman.*&#039;&#039; 权限节点被设置为true时,子权限也会随之设置为 &#039;&#039;plugin.yml&#039;&#039;中的默认值. 然而当&#039;&#039;doorman.*&#039;&#039; 被设置为false时，所有子权限都会设置为与默认值相反的值（true-&amp;gt;false,false-&amp;gt;true）.&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设置你自己的权限  ===&lt;br /&gt;
&lt;br /&gt;
如果你希望想知道更多有关开发你自己的权限插件的事情(即是设置权限的管理类插件) ，那么请前往查看教程&amp;amp;nbsp;[[Developing a permissions plugin]].&lt;br /&gt;
&lt;br /&gt;
== Scheduling Tasks and Background Tasks  ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 方块操控&amp;lt;br&amp;gt;  ==&lt;br /&gt;
&lt;br /&gt;
这是一个很容易的方法创建、获取或修改一个方块, 例如当你想要修改往你头上数第五格的方块时, 首先你需要做的是获取你当前的坐标点, 然后往你当前的坐标点的Y轴加上五, 获取目标方块后我们便能够很容易地修改它了, 例子如下:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@EventHandler&lt;br /&gt;
public void onPlayerMove(PlayerMoveEvent event) {&lt;br /&gt;
    // 得到玩家当前的坐标.&lt;br /&gt;
    Location loc = event.getPlayer().getLocation();&lt;br /&gt;
    // 设置目标坐标为玩家头顶 5 格处.&lt;br /&gt;
    loc.setY(loc.getY() + 5);&lt;br /&gt;
    // 得到目标坐标的方块&lt;br /&gt;
    Block b = loc.getBlock();&lt;br /&gt;
    // 将目标坐标的方块更改为石头 (stone).&lt;br /&gt;
    b.setType(Material.STONE);&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
上面的代码获取了玩家的所在位置，获取头上的第五个方块，并且放上一个石头（stone）。 Note that once you have a &amp;lt;code&amp;gt;Block&amp;lt;/code&amp;gt;, there are other things you can do besides set its type. 你可以查阅JavaDoc获取更多的东西。&lt;br /&gt;
&lt;br /&gt;
你可以用这样相似的观念去生成一些建筑，并且还可以用编程的方式通过算法生成一些独特的方块（这里指由很多个方块拼在一起的四四方方的方块）。比如，去生成一个实心的方块建筑，你可能在生成建筑的时候还需要用到&amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;循环，去一遍遍循环来生成这些建筑，填满这些建筑等。&lt;br /&gt;
下面，是一个在指定坐标生成一个实心的方块建筑的实例方法：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void generateCube(Location loc, int length) {&lt;br /&gt;
    // 放置这个方块最中间的方块并且获取这个方块的坐标。&lt;br /&gt;
    // Uses getBlockN() instead of getN() to avoid casting to an int later.&lt;br /&gt;
    int x1 = loc.getBlockX(); &lt;br /&gt;
    int y1 = loc.getBlockY();&lt;br /&gt;
    int z1 = loc.getBlockZ();&lt;br /&gt;
&lt;br /&gt;
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.&lt;br /&gt;
    int x2 = x1 + length;&lt;br /&gt;
    int y2 = y1 + length;&lt;br /&gt;
    int z2 = z1 + length;&lt;br /&gt;
&lt;br /&gt;
    World world = loc.getWorld();&lt;br /&gt;
&lt;br /&gt;
    // Loop over the cube in the x dimension.&lt;br /&gt;
    for (int xPoint = x1; xPoint &amp;lt;= x2; xPoint++) { &lt;br /&gt;
        // Loop over the cube in the y dimension.&lt;br /&gt;
        for (int yPoint = y1; yPoint &amp;lt;= y2; yPoint++) {&lt;br /&gt;
            // Loop over the cube in the z dimension.&lt;br /&gt;
            for (int zPoint = z1; zPoint &amp;lt;= z2; zPoint++) {&lt;br /&gt;
                // Get the block that we are currently looping over.&lt;br /&gt;
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);&lt;br /&gt;
                // 设置方块ID为57号(钻石块!)&lt;br /&gt;
                currentBlock.setType(Material.DIAMOND_BLOCK); //这里下面会提到&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
这个方法将会在程序给予这个方法高度和起始坐标（最中间的方块的坐标）时，构建一个3D的方块建筑。简单的删除方块也可以使用这个方法的思路实现，但是，但是设置方块的时候，记得把上面标注会提到的那行，设置为物品ID为0的方块(空气方块)。&amp;lt;br&amp;gt; &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== (玩家)操控背包  ==&lt;br /&gt;
&lt;br /&gt;
这个部分大部分讲的是操控玩家的背包。但是在某些情况下，操控箱子里的内容与此相同，当然得在你能找到获取箱子里内容的方法才可以。&amp;amp;nbsp;:P. 这是一个操控玩家背包的一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void onPlayerJoin(PlayerJoinEvent evt) {&lt;br /&gt;
    Player player = evt.getPlayer(); // 当玩家加入游戏&lt;br /&gt;
    PlayerInventory inventory = player.getInventory(); // 获取玩家背包列表&lt;br /&gt;
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // 生成一组钻石&lt;br /&gt;
        &lt;br /&gt;
    if (inventory.contains(itemstack)) {&lt;br /&gt;
        inventory.addItem(itemstack); // 将一组钻石放到玩家的背包里&lt;br /&gt;
        player.sendMessage(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;); //向玩家发送消息(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
所以，上面代码里面的 onPlayerJoin 方法中，我们首先构建了一些变量，让我们的工作变得更容易一些。变量分别是： player, inventory and itemstack。 inventory 变量是玩家的背包内容，itemstack是那64个钻石的物品栈。然后我们判断了玩家是不是有一组钻石。如果有，我们就用inventory.addItem(itemstack)方法再给玩家一组钻石，并且给他发送一个消息“Wow！你看上去很土豪啊！”。 所以操控背包并不像我们想象的那么难，如果我们想实现让玩家进入游戏，检测到一组钻石后删掉这组钻石，只需要把代码里的inventory.addItem(itemstack)替换成inventory.remove(itemstack)。获取你还得把“Wow！你看上去很土豪啊！”改成“Wow！你看上去好穷啊！”2333333。&lt;br /&gt;
&lt;br /&gt;
== 控制物品 ==&lt;br /&gt;
&lt;br /&gt;
当你需要写一个对物品们做出行为的代码时，你要使用ItemStack类来查阅有关那个栈的所有设置信息。&lt;br /&gt;
&lt;br /&gt;
=== 附魔属性 ===&lt;br /&gt;
&lt;br /&gt;
你想给一个物品附魔，你首先需要了解[http://www.minecraftwiki.net/wiki/Data_values Item Code]和[http://www.minecraftwiki.net/wiki/Talk:Enchanting#Effect_IDs Effect ID].  Enchantments他们自己不能例示(new Enchantment() won&#039;t work) 因为他们是抽象的。所以你必须要用一个EnchantmentWrapper。如果你想去附魔物品，还不想在默认的SMP里附魔，可以使用addEnchantment()里面的addUnsafeEnchantment()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
int itemCode = 280;  //use the item code you want here&lt;br /&gt;
int effectId = 20;  //use the enchantment code you want here&lt;br /&gt;
int enchantmentLevel = 100;&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(itemCode);  //new item of item code&lt;br /&gt;
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id&lt;br /&gt;
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //给物品附魔&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ItemMeta ===&lt;br /&gt;
You can set the display name of an item by doing this.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
String myDisplayName = &amp;quot;Awesome Sword&amp;quot;; //这里是保存了你想要显示的名字&lt;br /&gt;
 &lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你要更改的方块&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //得到方块的元数据&lt;br /&gt;
im.setDisplayName(myDisplayName); //设置它显示的名字&lt;br /&gt;
myItem.setItemMeta(im); //保存更改&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
You can also set the lores of an item. The lores are the small annotations on an item, like &amp;quot;+5 attack damage&amp;quot; on a stone sword.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;String&amp;gt; lores = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
lores.add(&amp;quot;Example lore&amp;quot;, &amp;quot;this one comes on line 2&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again&lt;br /&gt;
im.setLore(lores); //add the lores of course&lt;br /&gt;
myItem.setItemMeta(im); //give the item the new itemmeta&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maps, and Sets, and Lists, Oh My! &amp;lt;ref&amp;gt;标题中Map,Set,List均为Java类集中的接口，故不翻译&amp;lt;/ref&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
除了Map类和HashMap类，Java中还提供了其它的数据结构类集。 &lt;br /&gt;
&lt;br /&gt;
他们提供这些不同的类，是因为有些时候，使用Map类并不合适。&amp;lt;nowiki&amp;gt;[[Java data structure classes]]&amp;lt;/nowiki&amp;gt;在这个条目中，有对Java数据框架类集更深入的讲述。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;=== HashMaps概览以及如何使用此接口  ===&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;Keep in mind to never use a player in a hashmap!&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;&amp;lt;ref&amp;gt;下面有讲解。&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
应在HashMap中使用字符串.，使用 &amp;quot;p.getName()&amp;quot; 来添加，移除或是检查列表中是否含有某个玩家.&lt;br /&gt;
&lt;br /&gt;
将玩家当作object储存在HashMap中会造成大量内存泄漏.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;adam&amp;quot; as the key and assigned a value of &amp;quot;a&amp;quot; to it. That would work as intended, but then say afterwards I wanted to assign the value of &amp;quot;b&amp;quot; to key &amp;quot;adam&amp;quot; I would be able to and would get no errors but the value of &amp;quot;a&amp;quot; assigned to key &amp;quot;adam&amp;quot; in the HashMap would be overwritten because HashMaps cannot contain duplicate values. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Defining a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public Map&amp;lt;KeyType, DataType&amp;gt; HashMapName = new HashMap&amp;lt;&amp;gt;(); //Example syntax&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Example Declaration&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; pluginEnabled = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; isGodMode = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So inside on command you&#039;ll need this, the function name can be different but for the sake of simplicity it&#039;s best if you keep it the same. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;Player player = (Player) sender;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
togglePluginState(player);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void togglePluginState(Player player) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Notice how we use the player name as the key here,&lt;br /&gt;
&lt;br /&gt;
    // not the player object&lt;br /&gt;
&lt;br /&gt;
    String playerName = player.getName();&lt;br /&gt;
&lt;br /&gt;
    if (pluginEnabled.containsKey(playerName)) {&lt;br /&gt;
&lt;br /&gt;
        if (pluginEnabled.get(playerName)) {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, false);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin disabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, true);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    } else {&lt;br /&gt;
&lt;br /&gt;
        pluginEnabled.put(playerName, true); //If you want plugin disabled by default change this value to false.&lt;br /&gt;
&lt;br /&gt;
        player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== More Ideas for HashMaps  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A HashMap (or really any kind of Map in Java) is an association. It allows quick and efficient lookup of some sort of &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;value&#039;&#039;&#039;, given a unique &#039;&#039;&#039;key&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;. Anywhere this happens in your code, a Map may be your solution. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here are a few other ideas which are ideally suited to using Maps. As you will see, it doesn&#039;t have to be data that you store per player, but can be any kind of data that needs to be &amp;quot;translated&amp;quot; from one form to another. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Data Value Lookups  =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Integer&amp;gt; wool_colors = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;orange&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;magenta&amp;quot;, 2);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;light blue&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
   ..&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;black&amp;quot;, 15);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this in response to user commands - turn &amp;quot;green&amp;quot; into 13&lt;br /&gt;
&lt;br /&gt;
int datavalue = 0;&lt;br /&gt;
&lt;br /&gt;
if (wool_colors.containsKey(argument)) {&lt;br /&gt;
&lt;br /&gt;
    datavalue = wool_colors.get(argument);&lt;br /&gt;
&lt;br /&gt;
} else {&lt;br /&gt;
&lt;br /&gt;
    try { datavalue = Integer.parseInt(argument); }&lt;br /&gt;
&lt;br /&gt;
    catch (Exception e) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Saving/Loading a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you don&#039;t want an administrator to edit the data manually&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you need to save data in binary format (too complex to organize for YAML)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you want to avoid parsing block names and/or other objects from freeform text&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is very simple way how to save any HashMap. You can replace HashMap&amp;amp;amp;lt;String, Integer&amp;amp;amp;gt; with any type of HashMap you want. Let&#039;s take an example HashMap with code to save it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        HashMap&amp;lt;String, Integer&amp;gt; mapToSave = new HashMap&amp;lt;String,Integer&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
        public void save(HashMap&amp;lt;String, Integer&amp;gt; map, String path) {&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(map);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
//Handle I/O exceptions&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
save(mapToSave, getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see it&#039;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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public HashMap&amp;lt;String, Integer&amp;gt; load(String path) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
Object result = ois.readObject();&lt;br /&gt;
&lt;br /&gt;
//you can feel free to cast result to HashMap&amp;lt;String, Integer&amp;gt; if you know there&#039;s that HashMap in the file&lt;br /&gt;
&lt;br /&gt;
return (HashMap&amp;lt;String, Integer&amp;gt;)result;&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HashMap&amp;lt;Integer, String&amp;gt; loadedMap;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
String path = getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
File file = new File(path);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if (file.exists()) { // check if file exists before loading to avoid errors!&lt;br /&gt;
&lt;br /&gt;
loadedMap  = load(path);&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
You can use this &amp;quot;API&amp;quot; for saving/loading HashMaps, ArrayLists, and all Objects which implement Serializable or Externalizable interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;/** SLAPI = Saving/Loading API&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 * API for Saving and Loading Objects.&lt;br /&gt;
&lt;br /&gt;
 * Everyone has permission to include this code in their plugins as they wish :)&lt;br /&gt;
&lt;br /&gt;
 * @author Tomsik68&amp;lt;tomsik68@gmail.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
public class SLAPI&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; void save(T obj,String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(obj);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; T load(String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
T result = (T)ois.readObject();&lt;br /&gt;
&lt;br /&gt;
ois.close();&lt;br /&gt;
&lt;br /&gt;
return result;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Example implementation of this API: &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;I&#039;m skipping some part of code in this source&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public class Example extends JavaPlugin {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
private ArrayList&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onEnable() {&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
list = SLAPI.load(&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                //handle the exception&lt;br /&gt;
&lt;br /&gt;
                e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onDisable()	{&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
SLAPI.save(list,&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                 e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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&#039;re writing your own data object classes, and you may want to save their state using this technique, you should read about Java&#039;s Serializable or Externalizable interface. The only difference between Externalizable and Serializable is, that Serializable automatically takes all of class&#039;s fields and tries to serialize them, while Externalizable allows you to define method for reading and writing the Object. It&#039;s easy to add to your code, and it will make your data persistent with very little work required. No more parsing!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note #2: This API doesn&#039;t support changes. Once you change something in the class, data files saved with older version of your plugin won&#039;t load correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Tips &amp;amp; Examples =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.) Simplify your save structure&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.) Save version number along with data&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always remember, that you don&#039;t know what you&#039;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&#039;t know!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.) Migrate older files&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your plugin finds older version of some file, it should update the file accordingly and change version number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 元数据  ==&lt;br /&gt;
&lt;br /&gt;
Bukkit正在尝试尽可能的让制作插件变得更简单，所以带有一个键值例如Player, Entity, World，甚至是一个block的HashMaps都可以用元数据取缔掉。元数据是一些替代HashMap的方式。他可以允许你去自定义&amp;quot;fields&amp;quot;给Players, Entities, Worlds and Blocks.这些东西都是元数据类的一个成员(详见[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&#039;re creating an economy plugin, you would need a HashMap of Player and Float or Double. 如果用了元数据，你不必去做! You just attach to player new metadata 值, and that&#039;s it!&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== 为何要去使用元数据  ===&lt;br /&gt;
&lt;br /&gt;
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps. &lt;br /&gt;
* 元数据可以用来跟其他插件分享信息与数据。&lt;br /&gt;
&lt;br /&gt;
=== 为什么不去使用元数据&amp;lt;br&amp;gt;  ===&lt;br /&gt;
&lt;br /&gt;
* Slightly more difficult to get the value.&lt;br /&gt;
* It is not saved on shutdown (but then again, neither are any Maps that you create).&lt;br /&gt;
&lt;br /&gt;
=== 获取 &amp;amp;amp; 设置元数据  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {&lt;br /&gt;
  object.setMetadata(key, new FixedMetadataValue(plugin,value));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Object getMetadata(Metadatable object, String key, Plugin plugin) {&lt;br /&gt;
  List&amp;lt;MetadataValue&amp;gt; values = object.getMetadata(key);  &lt;br /&gt;
  for (MetadataValue value : values) {&lt;br /&gt;
     // Plugins are singleton objects, so using == is safe here&lt;br /&gt;
     if (value.getOwningPlugin() == plugin) {&lt;br /&gt;
        return value.value();&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
  return null;&lt;br /&gt;
}&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note: If you&#039;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.&lt;br /&gt;
&lt;br /&gt;
== Databases  ==&lt;br /&gt;
&lt;br /&gt;
Sometimes flat files aren&#039;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).&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there&#039;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!) &lt;br /&gt;
&lt;br /&gt;
=== SQLite  ===&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;s very straightforward and shouldn&#039;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]. &lt;br /&gt;
&lt;br /&gt;
SQLite is great for very simple databases, because there&#039;s no server concerns to set up. Just make a few calls to create a new database and table. It&#039;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&#039;s often easiest and fastest to get the SQL basics squared away with SQLite, even if you &amp;quot;graduate&amp;quot; to a more server-class database engine later.&lt;br /&gt;
&lt;br /&gt;
=== MySQL  ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 部署你的插件  ==&lt;br /&gt;
&lt;br /&gt;
 总之你写好的你的插件,如何把它变成一个可安装在服务端的jar文件呢?&lt;br /&gt;
 &lt;br /&gt;
 首先我们需要建立一个水桶服务器。 [http://wiki.bukkit.org/Setting_up_a_server 在这里] 能找到开服相关信息.接下来你需要把插件导出为一个.jar文件，&lt;br /&gt;
 &lt;br /&gt;
 这样你就能在你的服务器里运行它。 在Eclipse里, 右键工程，点击&#039;&#039;Run as &amp;gt; Maven install&#039;&#039;即可:&lt;br /&gt;
&lt;br /&gt;
[[Image:Maveninstall.png]]&lt;br /&gt;
&lt;br /&gt;
 将来呢, 当你对插件进行了改动后,在做上述操作之前，如果你想删除之前的.jar文件，你可以右键工程，点击 &#039;&#039;Run as &amp;gt; Maven clean&#039;&#039; 。 &lt;br /&gt;
 &lt;br /&gt;
 如果你在编译插件时遇到了问题,  请检查你的JDK是否正确安装， 并浏览[[建立工作区]]条目。&lt;br /&gt;
 &lt;br /&gt;
 如果你在Eclipse控制台看到了和JDK有关的错误，你可能需要手动调整JDK,因为Eclipse的检测可能发生了错误。 &lt;br /&gt;
 &lt;br /&gt;
 点击&#039;&#039;Window -&amp;gt; Preferences&#039;&#039;, 然后是&#039;&#039;Java -&amp;gt; Installed JREs&#039;&#039;。选中你最近安装的JDK作为Java运行环境（JRE）并添加, 并取消掉原来那个报错的JDK:&lt;br /&gt;
&lt;br /&gt;
[[Image:Jrelocation.png]]&lt;br /&gt;
&lt;br /&gt;
 如果你的工程成功被编译,JAR文件会生产在 &#039;&#039;target&#039;&#039; 目录下，具体在Eclipse的工作空间里名字为工程名的文件夹中。&lt;br /&gt;
 &lt;br /&gt;
 这个JAR文件就是一个能正常工作的Bukkit插件了。当然前提是你的plugin.yml里没出差错。&lt;br /&gt;
 &lt;br /&gt;
 你可以把这个jar文件丢到服务器的plugins文件夹里,重载或重启服务器,就可以测试你的新插件啦。&lt;br /&gt;
 &lt;br /&gt;
 为了连接上使用你自己的电脑开的服务器 ,在多人游戏的ip里填上你的本地ip即可。&lt;br /&gt;
 &lt;br /&gt;
 如果出现了错误而你又不能解决的话, 试试这里[http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], &lt;br /&gt;
 &lt;br /&gt;
 在这里提问[http://wiki.bukkit.org/IRC bukkitdev IRC channel],或者重新阅读wiki。&lt;br /&gt;
 &lt;br /&gt;
 当你做完了一个实用的插件,可以考虑把它发表在这里[http://dev.bukkit.org/ dev.bukkit]（为了Bukkit社区的访问量）。&lt;br /&gt;
 &lt;br /&gt;
 以上可知,JAR格式是默认压缩生成的(JAR文件基于ZIP文件的格式)。这么说来，在上传插件到Bukkit上时把JAR文件压缩成ZIP只会增加文件体积罢了。&lt;br /&gt;
 &lt;br /&gt;
 而且呢, 放在JAR中的config文件可以在检测不到插件目录（插件自动生成的文件夹，名字与插件名相同，里面是该插件的配置）下的config文件时自动生成。&lt;br /&gt;
 &lt;br /&gt;
 所以我们一般不把jar文件转换成其他格式。&lt;br /&gt;
&lt;br /&gt;
== 导入其它插件 ==&lt;br /&gt;
你可能会希望编辑其它那些有源代码的插件. 如果插件在它的目录下有&#039;&#039;pom.xml&#039;&#039;(大多数知名插件,例如WorldEdit和Essentials都有),你可以通过点击&#039;&#039;File -&amp;gt; Import&#039;&#039;来把它作为一个工程导入, 然后打开&#039;&#039;Maven&#039;&#039;项并选择&#039;&#039;Existing Maven Projects&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
[[Image:Importmaven.png]]&lt;br /&gt;
&lt;br /&gt;
然后选择&#039;&#039;pom.xml&#039;&#039;所在的目录,这个工程就应该出现在你的侧边栏里了。就像平常一样编辑并编译它吧。&lt;br /&gt;
&lt;br /&gt;
== 小贴士  ==&lt;br /&gt;
&lt;br /&gt;
=== 使玩家着火 ===&lt;br /&gt;
&lt;br /&gt;
 Bukkit API有很多碉堡的功能。以下是实现一些功能的代码片段。 &lt;br /&gt;
&lt;br /&gt;
下面的代码运行一个玩家使另一个玩家着火。通过类似于&#039;&#039;&#039;/ignite Notch&#039;&#039;&#039;这样的指令，这个指令会让 Notch 着火。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     // 使用 equalsIgnoreCase() 来忽视大小写&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;ignite&amp;quot;)) {&lt;br /&gt;
         // 确保玩家只输入了一个自变量(要被点着的玩家名).&lt;br /&gt;
         if (args.length != 1) {&lt;br /&gt;
             // 当return false时，该指令的使用信息会显示出来。&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 确保命令发出者是个玩家.&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can set other players on fire.&amp;quot;);&lt;br /&gt;
             sender.sendMessage(&amp;quot;This is an arbitrary requirement for demonstration purposes only.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 获取那个要被点着的玩家. 请注意下标起始是0，不是1.&lt;br /&gt;
         Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
 &lt;br /&gt;
         // 确保这个玩家在线。&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 让这个玩家着火1000 ticks (一秒钟大概有20ticks,所以总共是50秒).&lt;br /&gt;
         target.setFireTicks(1000);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 干掉玩家  ===&lt;br /&gt;
&lt;br /&gt;
 和标题一样.这里是要干掉某个玩家。 &lt;br /&gt;
&lt;br /&gt;
在你的onCommand方法里使用这个: &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;KillPlayer&amp;quot;)) {&lt;br /&gt;
         Player target = sender.getServer().getPlayer(args[0]);&lt;br /&gt;
          // 确保玩家在线.&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         target.setHealth(0); &lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这里有个小扩展,会使用一个爆炸来杀死玩家: &lt;br /&gt;
&lt;br /&gt;
 float explosionPower = 4F; //爆炸等级 - TNT的爆炸等级默认是4F&lt;br /&gt;
 Player target = sender.getWorld().getPlayer(args[0]);&lt;br /&gt;
 target.getWorld().createExplosion(target.getLocation(), explosionPower);&lt;br /&gt;
 target.setHealth(0);&lt;br /&gt;
&lt;br /&gt;
=== 创造一个假爆炸  ===&lt;br /&gt;
&lt;br /&gt;
这段代码会生产一个看起来和听起来与TNT/苦力怕产生的爆炸一样的效果。但是不会对实体和方块产生破坏。 在保留爆炸的艺术的同时又削弱了爆炸的威力，真是太有用了（&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onExplosionPrime(ExplosionPrimeEvent event) {	&lt;br /&gt;
     Entity entity = event.getEntity();&lt;br /&gt;
 &lt;br /&gt;
     // 如果事件和点燃的TNT有关,执行一些代码（又略）&lt;br /&gt;
     if (entity instanceof TNTPrimed) {&lt;br /&gt;
         entity.getWorld().createExplosion(entity.getLocation(), 0);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 使一个玩家对另外一个玩家隐形  ===&lt;br /&gt;
&lt;br /&gt;
 通过输入指令，能让指令执行者对特定的玩家隐形。 然而其他人仍然能看到指令执行者。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;HideMe&amp;quot;) &amp;amp;&amp;amp; args.length == 1) {&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can use this command!&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         // 检测指令执行者是玩家后，我们便可以安全的进行强制转换类操作。&lt;br /&gt;
        Player s = (Player) sender;&lt;br /&gt;
 &lt;br /&gt;
        // 获取命令执行者指定的玩家&lt;br /&gt;
        Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
        if (target == null) {&lt;br /&gt;
            sender.sendMessage(&amp;quot;Player &amp;quot; + args[0] + &amp;quot; is not online.&amp;quot;);&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        // 将 s 从 target 的视野中隐藏起来.&lt;br /&gt;
        target.hidePlayer(s);&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 在玩家准星所指处生产闪电 ===&lt;br /&gt;
&lt;br /&gt;
如下代码运行手持鱼竿的玩家通过点击来生成闪电 (当然要用准星瞄准)。 真是个简单好玩的恶作剧（。&lt;br /&gt;
&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onPlayerInteractBlock(PlayerInteractEvent event) {&lt;br /&gt;
     Player player = event.getPlayer();&lt;br /&gt;
     if (player.getItemInHand().getType() == Material.FISHING_ROD) {&lt;br /&gt;
         // 在给定坐标中生成一道闪电. 在本例中, 这个坐标是玩家准星瞄准的地方.&lt;br /&gt;
         // 只能指向200格以内的坐标.&lt;br /&gt;
         player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 自动格式 ===&lt;br /&gt;
&lt;br /&gt;
 Eclipse提供了将你的代码自动转成Oracle规定的格式, 修正了不符合常规的缩进, 空格等等。 在侧边栏选中你的项目,然后选择&#039;&#039;Source -&amp;gt; Format&#039;&#039;即可对其进行更改。&lt;br /&gt;
&lt;br /&gt;
== 插件需求专区  ==&lt;br /&gt;
&lt;br /&gt;
http://forums.bukkit.org/forums/plugin-requests.13/&lt;br /&gt;
&lt;br /&gt;
== 范例文件和样板  ==&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/wpeTPx7N Example.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have any more questions on this matter, don&#039;t hesitate to visit the BukkitDev [[IRC]] channel and ask!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:教程]]&lt;br /&gt;
[[Category:大篇幅条目]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=1364</id>
		<title>Configuration API Reference</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Configuration_API_Reference&amp;diff=1364"/>
		<updated>2015-09-01T12:15:12Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​小小的翻译了一下&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{待翻译}}&lt;br /&gt;
&lt;br /&gt;
Configuration API是一个帮助程序员们更快地解析与编辑配置文件的设置工具，让人们觉得配置文件更简单更容易编辑。不管是什么，这个API可以帮助你更容易的存储你插件的信息。目前只有这个 [http://en.wikipedia.org/wiki/YAML YAML] configurations 可以用。然而，有了这个API，设计的能有更多的扩展空间，还能允许你实现其他的东西。&lt;br /&gt;
&lt;br /&gt;
Configuration API可以在 org.bukkit.configuration 和 org.bukkit.configuration.file 这俩包里找到。插件的版本如果创建在 1.1-R5之前，就只能用老的并且与此不同的方法了，这些东西在org.bukkit.util.configuration这个包里。这些实现方法彼此不兼容并且老的方法因为太OUT了，在新版中被删去了。&lt;br /&gt;
&lt;br /&gt;
This introduction assumes that you have some knowledge about proper object oriented design, Java, and the core design of Bukkit plugins. This page is not a substitute for the JavaDocs for the [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfiguration.html FileConfiguration Class]&lt;br /&gt;
&lt;br /&gt;
== Basic Topics  ==&lt;br /&gt;
&lt;br /&gt;
=== The Configuration Object  ===&lt;br /&gt;
&lt;br /&gt;
Your plugin extends [http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html JavaPlugin], and in doing so, you inherited methods and fields from JavaPlugin. The inherited method, &#039;&#039;[http://jd.bukkit.org/doxygen/d7/deb/classorg_1_1bukkit_1_1plugin_1_1java_1_1JavaPlugin.html#a79493f3bc9db2acbf9405c3a33e0acfd getConfig()]&#039;&#039; returns an object of type [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html FileConfiguration]. This is the object that represents &#039;&#039;config.yml&#039;&#039; inside your plugin&#039;s data folder.&lt;br /&gt;
&lt;br /&gt;
The first time &#039;&#039;getConfig()&#039;&#039; is invoked on your plugin, &#039;&#039;config.yml&#039;&#039; is loaded from disk, and default values are loaded from the jar. Subsequent invocations of &#039;&#039;getConfig()&#039;&#039; will return the existing &#039;&#039;FileConfiguration&#039;&#039; object that is in memory. Operations performed on this object will not be written to disk unless explicitly saved. Likewise, any modifications done after the file has been loaded will not be reflected in the object. If &#039;&#039;config.yml&#039;&#039; does not exist in your data folder, it is equivalent to an empty &#039;&#039;config.yml&#039;&#039;, and will load an empty &#039;&#039;FileConfiguration&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
{{warning}} if you assign the returned object from &#039;&#039;getConfig()&#039;&#039; DO NOT assign it to a static field&amp;lt;br&amp;gt; {{warning}} if you do the above, assign &#039;&#039;getConfig()&#039;&#039; to the variable AGAIN after a reloadConfig &lt;br /&gt;
{{note|it is better to just use &#039;&#039;getConfig()&#039;&#039; instead of assigning it to an instance variable}}&lt;br /&gt;
&lt;br /&gt;
=== Keys  ===&lt;br /&gt;
&lt;br /&gt;
A configuration file is organized into key value pairs where all keys are Strings. The value for the other keys may be a ConfigurationSection or a single piece of data. The &#039;&#039;getKeys(boolean)&#039;&#039; method returns the set of keys for the current &#039;&#039;FileConfigurationSection&#039;&#039;. The boolean value determines if the returned set is recursive, if true it will return the keys of the given section and their children keys, if false will only return keys of the given section. To get the keys of a particular section, the &#039;&#039;getKeys(boolean)&#039;&#039; method must be invoked on that particular section. To get the section you will have to invoke &#039;&#039;getConfigurationSection(String)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{{note|The getKeys method returns a [http://download.oracle.com/javase/1,5.0/docs/api/java/util/Set.html Set] of Strings}} &lt;br /&gt;
&lt;br /&gt;
=== Paths ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API uses Paths to form a unique key to value pairs. A path is the set of keys used to associate a value. Each level is separated by the path separator, which is by default the &#039;.&#039; (period). For example the following YAML file has the following set of paths.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;key: value&lt;br /&gt;
one:&lt;br /&gt;
  two: value&lt;br /&gt;
  three:&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
    - values&lt;br /&gt;
  four: &lt;br /&gt;
    five: value&lt;br /&gt;
  *:&lt;br /&gt;
    six: value&lt;br /&gt;
    seven: value&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*key&lt;br /&gt;
*one&lt;br /&gt;
*one.two&lt;br /&gt;
*one.three&lt;br /&gt;
*one.four&lt;br /&gt;
*one.four.five&lt;br /&gt;
*one.*&lt;br /&gt;
*one.*.six&lt;br /&gt;
*one.*.seven&lt;br /&gt;
&lt;br /&gt;
=== Default Values ===&lt;br /&gt;
&lt;br /&gt;
A default &#039;&#039;config.yml&#039;&#039; should be provided in your jar for users. In the event that a config.yml is missing or incomplete, values will be loaded from included &#039;&#039;config.yml&#039;&#039;. The provided file must be named &#039;&#039;config.yml&#039;&#039; and be placed in the same directory as your [[Plugin YAML|plugin.yml]]. The file should have the intended structure of your &#039;&#039;config.yml&#039;&#039;. This file can be copied as is into the datafolder by invoking saveDefaultConfig() on the Appropriate instance of JavaPlugin.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveDefaultConfig()&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If dynamic key-value pairs are required as default values, they can added as defaults to the configuration with invocations of &#039;&#039;addDefault(String, Object)&#039;&#039; and &#039;&#039;addDefaults(Map&amp;amp;lt;String,Object&amp;amp;gt;)&#039;&#039; methods. &lt;br /&gt;
&lt;br /&gt;
In certain cases if you wish to append new key-value pairs to an existing config.yml you can set the copyDefaults to true for the ConfigurationOptions object. &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().options().copyDefaults(true)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Creating a copy of config.yml ====&lt;br /&gt;
You can create a copy of config.yml from the jar into the plugin&#039;s data folder by invoking JavaPlugin&#039;s saveDefaultConfig() method. saveDefaultConfig() will not overwrite an existing file.&lt;br /&gt;
&lt;br /&gt;
=== Getting Values ===&lt;br /&gt;
&lt;br /&gt;
Reading values from the configuration involves invoking one of the many getter methods. A complete list of getters can be found [http://jd.bukkit.org/doxygen/dd/d7c/classorg_1_1bukkit_1_1configuration_1_1file_1_1FileConfiguration.html here]. Every getter method takes a configuration path detailed above. Some of the commonly used getter methods are as follows &lt;br /&gt;
&lt;br /&gt;
*getBoolean(String) &lt;br /&gt;
*getInt(String) &lt;br /&gt;
*getString(String) &lt;br /&gt;
*getList(String) &lt;br /&gt;
*getStringList(String)&lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
In the case of HashMaps as a value, they are treated differently than other forms of data. There is a restriction for Map types. It must use a String as a key, and the value but be either a boxed primitive, String, List, Map, or a ConfigurationSerializable type. They will lose their type.&lt;br /&gt;
&lt;br /&gt;
To get a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must must first be retrieved. You can return the configuration with  getConfigurationSection method. The getValues method will return the values in the ConfigurationSection as a map, it takes a boolean which controls if the nested maps will be returned in the map. You can obtain the original map by invoking getValues(false) on the returned ConfigurationSection. Due to the way Java handles generic classes, type information will be lost, thus a cast will need to be performed to set the original type information. The API makes no guarantees that the cast you perform will be safe.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().getConfigurationSection(&amp;quot;path.to.map&amp;quot;).getValues(false)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Setting Values  ===&lt;br /&gt;
&lt;br /&gt;
Writing values involves invoking the &#039;&#039;set(String, Object)&#039;&#039; method on an instance of Configuration. Unlike the different get methods that &#039;&#039;FileConfiguration&#039;&#039; has, there is only one set method. Not all objects can be set, only primitive types, &#039;&#039;String&#039;&#039;, &#039;&#039;Lists&#039;&#039;, and types that implement &#039;&#039;ConfigurationSerializable&#039;&#039;, such as &#039;&#039;Vector&#039;&#039; and &#039;&#039;ItemStack&#039;&#039;, can be set. To erase a value supply null as a parameter. All changes made by set will only affect the copy of the configuration in memory, and will not persist beyond restarting the server until the configuration is saved. Following are some example uses: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;// setting a boolean value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.boolean&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// setting a String&lt;br /&gt;
String stringValue = &amp;quot;Hello World!&amp;quot;;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.string&amp;quot;, stringValue);&lt;br /&gt;
&lt;br /&gt;
// setting an int value&lt;br /&gt;
int integerValue = 8;&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.integer&amp;quot;, integerValue);&lt;br /&gt;
&lt;br /&gt;
// Setting a List of Strings&lt;br /&gt;
// The List of Strings is first defined in this array&lt;br /&gt;
List&amp;lt;String&amp;gt; listOfStrings = Arrays.asList(&amp;quot;Hello World&amp;quot;, &amp;quot;Welcome to Bukkit&amp;quot;, &amp;quot;Have a Good Day!&amp;quot;);&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.list&amp;quot;, listOfStrings);&lt;br /&gt;
&lt;br /&gt;
// Setting a vector&lt;br /&gt;
// event is assumed to be an existing event inside an &amp;quot;onEvent&amp;quot; method.&lt;br /&gt;
Vector vector = event.getPlayer().getLocation().toVector();&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.vector&amp;quot;, vector);&lt;br /&gt;
&lt;br /&gt;
// Erasing a value&lt;br /&gt;
this.getConfig().set(&amp;quot;path.to.value&amp;quot;, null);&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==== HashMaps  ====&lt;br /&gt;
&lt;br /&gt;
When HashMaps are used as a value, they are treated slightly differently. The Map must parameterized with a String type for the key, and the value must be parameterized as a boxed primitive, String, List, Map, or a ConfigurationSerializable.&lt;br /&gt;
&lt;br /&gt;
While you can use the set method to directly set a HashMap to a key, you cannot directly retrieve the Map back with the get method after reading directly from disk. The [[#HashMaps|context above]] is to minimize unpredictability.&lt;br /&gt;
&lt;br /&gt;
To set a &#039;&#039;HashMap&#039;&#039;, a &#039;&#039;ConfigurationSection&#039;&#039; must be created for that HashMap. You can only set HashMap where the key is a string the the value is something that is ConfigurationSerializable. The createSectionMethod &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.getConfig().createSection(String path, Map&amp;lt; String, Object &amp;gt; map)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Saving the File  ===&lt;br /&gt;
&lt;br /&gt;
If make any changes to the&#039;&#039;FileConfiguration&#039;&#039; with set methods, or mutate any &#039;&#039;Lists&#039;&#039;, you will need to save the changes to disk if you wish to keep these changes after the plugin is disabled. To save the file to disk invoke the saveConfig method for your plugin, it will overwrite the file already there. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.saveConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reloading from Disk  ===&lt;br /&gt;
&lt;br /&gt;
If you suspect that users have made changes to the &#039;&#039;config.yml&#039;&#039; in the data folder, those changes are not reflected in memory. Invoke the &#039;&#039;reloadConfig()&#039;&#039; method of your plugin to load from the disk again. It will destroy all changes in memory. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;this.reloadConfig();&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics  ==&lt;br /&gt;
&lt;br /&gt;
The following are some more advanced topics, meant for more advanced plugins. If you only require the default &#039;&#039;config.yml&#039;&#039;, creating custom methods for reading, and saving, you will not need to go this far. &lt;br /&gt;
&lt;br /&gt;
=== Options  ===&lt;br /&gt;
&lt;br /&gt;
Every &#039;&#039;FileConfiguration&#039;&#039; instance is associated with a [http://jd.bukkit.org/apidocs/index.html?org/bukkit/configuration/file/FileConfigurationOptions.html FileConfigurationOptions] object. The &#039;&#039;FileConfigurationOptions&#039;&#039; object controls the behavior of the &#039;&#039;FileConfiguration&#039;&#039; it is associated with. &#039;&#039;FileConfiguration&#039;&#039;&#039;s &#039;&#039;options()&#039;&#039; method returns the &#039;&#039;FileConfigurationOption&#039;s&#039;&#039; responsible for it. With it you can check and set each option. There are currently four options. Be aware that the methods are overloaded, for example &#039;&#039;copyDefaults()&#039;&#039; which returns a boolean and &#039;&#039;copyDefaults(boolean)&#039;&#039; which returns it self, but has a side effect which changes the state. &lt;br /&gt;
&lt;br /&gt;
==== CopyDefaults  ====&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;copyDefaults&#039;&#039; option changes the behavior of Configuration&#039;s save method. By default, the defaults of the configuration will not be written to the target save file. If set to true, it will write out the default values, to the target file. However, once written, you will not be able to tell the difference between a default and a value from the configuration. &lt;br /&gt;
&lt;br /&gt;
==== PathSeperator  ====&lt;br /&gt;
&lt;br /&gt;
PathSeperator changes the character that is used to separate the different levels of the configuration. By default it is the &amp;quot;.&amp;quot; (period) but it can be changed to any char. &lt;br /&gt;
&lt;br /&gt;
==== Header  ====&lt;br /&gt;
&lt;br /&gt;
Header is the comment block at the top of a YAML file, it is applied to the save output. The header is the only comment that Configuration API knows how to copy. &lt;br /&gt;
&lt;br /&gt;
==== copyHeader  ====&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;copyHeader()&#039;&#039; returns true then the header will be copied on save, from the default source. &lt;br /&gt;
&lt;br /&gt;
=== Arbitrary Configurations  ===&lt;br /&gt;
&lt;br /&gt;
If you require additional YAML files, for storing configuration information or persisting additional game information you will need to write your own methods for accessing the additional configuration files. Modeled after JavaPlugin getConfig, reloadConfig, saveConfig methods, the following is an example how to write your own methods to read and save to custom configuration files. Since these config files belong to your plugin, you can put this method in your main class so that you can have the same access as you have with config.yml. You will have to write a set of these methods for each YAML file. The advantage here, is that you can use each set in the same manner as the provided methods for the default config.yml. Alternately, adding additional methods can keep the method count lower and allow access to multiple files.&lt;br /&gt;
&lt;br /&gt;
==== Mirroring the JavaPlugin implementation ====&lt;br /&gt;
JavaPlugin implements methods for config.yml. A plugin needs to implement its own methods to access configuration files unique to the plugin. After implementing the methods for the plugin, they can be invoked in the same context as the inherited &#039;&#039;getConfig()&#039;&#039;, &#039;&#039;reloadConfig()&#039;&#039;, &#039;&#039;saveConfig()&#039;&#039;, and &#039;&#039;saveDefaultConfig()&#039;&#039; methods from JavaPlugin. The following can be made into a single class which allows access to any yaml file. Such a class can be found [https://gist.github.com/3174347 here]&lt;br /&gt;
&lt;br /&gt;
First you will need to declare two fields and initialize them to null for each of the custom configuration files. One to hold the &#039;&#039;FileConfiguration&#039;&#039; object and one to hold the &#039;&#039;File&#039;&#039; object. The File object represents the file on the disk, and the FileConfiguration represents the contents of the configuration. &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;private FileConfiguration customConfig = null;&lt;br /&gt;
private File customConfigFile = null;&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Reloading =====&lt;br /&gt;
Then, write the method that is responsible for loading the config from disk. It will load the file, and search the jar for a default customConfig.yml. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void reloadCustomConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
    customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    customConfig = YamlConfiguration.loadConfiguration(customConfigFile);&lt;br /&gt;
&lt;br /&gt;
    // Look for defaults in the jar&lt;br /&gt;
    Reader defConfigStream = new InputStreamReader(this.getResource(&amp;quot;customConfig.yml&amp;quot;), &amp;quot;UTF8&amp;quot;);&lt;br /&gt;
    if (defConfigStream != null) {&lt;br /&gt;
        YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);&lt;br /&gt;
        customConfig.setDefaults(defConfig);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Getting =====&lt;br /&gt;
Next, you need to write the getter method. Check if &#039;&#039;customConfig&#039;&#039; is null, if it is load from disk. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public FileConfiguration getCustomConfig() {&lt;br /&gt;
    if (customConfig == null) {&lt;br /&gt;
        reloadCustomConfig();&lt;br /&gt;
    }&lt;br /&gt;
    return customConfig;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Saving =====&lt;br /&gt;
Finally, write the save method, which saves changes and overwrites the file on disk. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void saveCustomConfig() {&lt;br /&gt;
    if (customConfig == null || customConfigFile == null) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
        getCustomConfig().save(customConfigFile);&lt;br /&gt;
    } catch (IOException ex) {&lt;br /&gt;
        getLogger().log(Level.SEVERE, &amp;quot;Could not save config to &amp;quot; + customConfigFile, ex);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Implementation for Defaults =====&lt;br /&gt;
Optionally, you may want to write a method that mimics JavaPlugin&#039;s saveDefaultConfig() method.  &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void saveDefaultConfig() {&lt;br /&gt;
    if (customConfigFile == null) {&lt;br /&gt;
        customConfigFile = new File(getDataFolder(), &amp;quot;customConfig.yml&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    if (!customConfigFile.exists()) {            &lt;br /&gt;
         plugin.saveResource(&amp;quot;customConfig.yml&amp;quot;, false);&lt;br /&gt;
     }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Serializing and Deserializing Objects  ===&lt;br /&gt;
&lt;br /&gt;
The Configuration API, as mentioned above can store Java objects that implement the &#039;&#039;ConfigurationSerializable&#039;&#039; Interface.  Object serialization facilitates easy saving and loading so plugin authors can focus on other parts of their plugin. It greatly simplifies tasks such as storing a &#039;&#039;[http://jd.bukkit.org/doxygen/da/dac/classorg_1_1bukkit_1_1Location.html Location]&#039;&#039; in YAML, a developer can serialize a wrapper class, which provide methods to retrieve a &#039;&#039;Location&#039;&#039;.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Classes, in addition to implementing the &#039;&#039;ConfigurationSerializable&#039;&#039; interface must also implment one of the following as noted in the Javadoc, so that they can be serialized by the API: &lt;br /&gt;
&lt;br /&gt;
*A constructor that accepts a single &#039;&#039;Map&#039;&#039;. &lt;br /&gt;
*A static method &amp;quot;deserialize&amp;quot; that accepts a single &#039;&#039;Map&#039;&#039; and returns the class. &lt;br /&gt;
*A static method &amp;quot;valueOf&amp;quot; that accepts a single Map and returns the class.&lt;br /&gt;
&lt;br /&gt;
In order for a serialized object to be deserialized, it must also be registered with ConfigurationSerialization. The static registerClass method must be invoked once per class that has been serialized.&lt;br /&gt;
&lt;br /&gt;
This statement must be placed in your onEnable method or some other location that gets called every time your plugin is initialized:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
{{warning}}Do not use a static block to execute the above; if you do so, it will not be called a second time when &amp;lt;code&amp;gt;/reload&amp;lt;/code&amp;gt; is used and you will encounter errors due to it not being registered!&lt;br /&gt;
&lt;br /&gt;
==== Aliases  ====&lt;br /&gt;
&lt;br /&gt;
When classes are serialized they are marked with their fully qualified name.&lt;br /&gt;
You can provide an alias to your class so that it does not serialize with the fully qualified name of your class, but the alias instead. You provide the alias with the &#039;&#039;SerializableAs&#039;&#039; annotation to the class implementing &#039;&#039;ConfigurationSerializable&#039;&#039;. &amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;@SerializableAs(String)&amp;lt;/source&amp;gt; When registering a class with an alias, the alias must be provided on registration. &amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;ConfigurationSerialization.registerClass(Class&amp;lt;? extends ConfigurationSerializable&amp;gt;, String)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Example Use  ==&lt;br /&gt;
&lt;br /&gt;
Below is the an example plugin that uses the new Configuration API to be display messages as an MOTD as players join, and for the player to retrieve the rules on command. It does not follow proper style and plugin layout to keep the number of lines to a minimum.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;import java.util.*;&lt;br /&gt;
import org.bukkit.command.*;&lt;br /&gt;
import org.bukkit.event.*;&lt;br /&gt;
import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
import org.bukkit.configuration.file.FileConfiguration;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class SimpleMOTD extends JavaPlugin {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void onEnable() {&lt;br /&gt;
        // Save a copy of the default config.yml if one is not there&lt;br /&gt;
        this.saveDefaultConfig();&lt;br /&gt;
        &lt;br /&gt;
        // Register a new listener&lt;br /&gt;
        getServer().getPluginManager().registerEvents(new Listener() {&lt;br /&gt;
            &lt;br /&gt;
            @EventHandler&lt;br /&gt;
            public void playerJoin(PlayerJoinEvent event) {&lt;br /&gt;
                // On player join send them the message from config.yml&lt;br /&gt;
                event.getPlayer().sendMessage(this.getConfig().getString(&amp;quot;message&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
        }, this);&lt;br /&gt;
&lt;br /&gt;
        // Set the command executor for the rules command&lt;br /&gt;
        this.getCommand(&amp;quot;rules&amp;quot;).setExecutor(new CommandExecutor() {&lt;br /&gt;
            &lt;br /&gt;
            public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {&lt;br /&gt;
                // On command send the rules from config.yml to the sender of the command&lt;br /&gt;
                List&amp;lt;String&amp;gt; rules = this.getConfig().getStringList(&amp;quot;rules&amp;quot;);&lt;br /&gt;
                for (String s : rules)&lt;br /&gt;
                    sender.sendMessage(s);&lt;br /&gt;
                }&lt;br /&gt;
                return true;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; The default config.yml that is in the plugin&#039;s jar&lt;br /&gt;
&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;# default config.yml&lt;br /&gt;
message: Hello World and Welcome! :)&lt;br /&gt;
rules:&lt;br /&gt;
  - Play Nice&lt;br /&gt;
  - Respect others&lt;br /&gt;
  - Have Fun&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1345</id>
		<title>Bukkit/插件开发教程</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1345"/>
		<updated>2015-08-31T07:58:58Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* Metadata */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
{{模板:待完善}}&lt;br /&gt;
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]&lt;br /&gt;
== 介绍  ==&lt;br /&gt;
&lt;br /&gt;
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.&lt;br /&gt;
&lt;br /&gt;
== 学习Java  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;理解这些教程需要具备Java这门编程语言的基本知识.&#039;&#039;&#039;如果你刚开始学习Java或者需要重温一下相关知识，下面有一个并不完整的网站列表可供参考.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;[http://docs.oracle.com/javase/tutorial/ Oracle&#039;s Java Tutorials]&amp;lt;/big&amp;gt; - 官方教程&lt;br /&gt;
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程&lt;br /&gt;
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程&lt;br /&gt;
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书&lt;br /&gt;
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.&lt;br /&gt;
&lt;br /&gt;
== 开发环境  ==&lt;br /&gt;
&lt;br /&gt;
在编写一个插件前 (或学习Java前) 你需要配置一个开发者环境，这个包括但不限于安装IDE (Integrated Development Environment，集成开发环境)。接下来的教程中有关于Eclipse上IDE的操作指南。&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;更多信息，请参见 [[Bukkit/插件开发教程-建立工作区|建立工作区]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
你&#039;&#039;&#039;必须&#039;&#039;&#039;下载&#039;&#039;&#039;Java&#039;&#039;&#039;开发者使用的Eclipse构建版本, &#039;&#039;&#039;不是&#039;&#039;&#039; &#039;&#039;&#039;Java EE&#039;&#039;&#039;开发者的版本。 Java EE 开发者的版本不包含此教程需要的Maven支持。&lt;br /&gt;
&lt;br /&gt;
== 新建插件开发项目  ==&lt;br /&gt;
=== 创建一个新的项目 ===&lt;br /&gt;
&lt;br /&gt;
在开始工作之前，你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击&#039;&#039;File -&amp;gt; New -&amp;gt; Project:&#039;&#039;来创建一个新的项目.&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject.png]] &lt;br /&gt;
&lt;br /&gt;
现在,打开&#039;&#039;Maven&#039;&#039;文件夹, 然后选择&#039;&#039;Maven Project&#039;&#039;.点击next,之后在下一个菜单中选择&#039;&#039;Create a simple project&#039;&#039;, 再次点击&#039;&#039;Next&#039;&#039;:&lt;br /&gt;
如果你看不到&#039;&#039;Maven&#039;&#039;文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject2.png]] &lt;br /&gt;
&lt;br /&gt;
现在，你需要给你的组用户命名，就像下面这样:&lt;br /&gt;
* 如果你拥有一个域名，package则填写逆转的域名地址. &lt;br /&gt;
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]&lt;br /&gt;
** 避免使用一个不属于你自己的域名.&lt;br /&gt;
* 没有域名? 这里有几种方法可供选择。&lt;br /&gt;
*# 在资源管理站点创建一个用户，比如GitHub或是sourceforge&lt;br /&gt;
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 使用你的邮箱.  例如: &amp;lt;username&amp;gt;@gmail.com格式的邮箱应输入com.gmail.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 最不济的方法.：  使用独特的组名命名方式，这应当是你最后的解决方法。&lt;br /&gt;
&lt;br /&gt;
以下几个地址不能作为package中的地址前缀:&lt;br /&gt;
* org.bukkit&lt;br /&gt;
* net.bukkit&lt;br /&gt;
* com.bukkit&lt;br /&gt;
* net.minecraft&lt;br /&gt;
&lt;br /&gt;
完成基础的组名以后，你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 &#039;&#039;TestPlugin&#039;&#039;的插件，那么完整的组名是&#039;&#039;io.github.&amp;lt;username&amp;gt;&#039;&#039;, 你的工程名也是 &#039;&#039;TestPlugin&#039;&#039;. 至于版本，默认即可，稍后可以修改。&lt;br /&gt;
完成向导:&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject3.png]] &lt;br /&gt;
&lt;br /&gt;
如果这是你首次使用Eclipse, 点击右上角的&amp;quot;X&amp;quot; 来关闭Welcome提示页面. 现在，你的窗口视图看起来就像下面的图片这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Eclipsemain.png]] &lt;br /&gt;
&lt;br /&gt;
点击工程名称右边的箭头来进行下一步，现在我们正式开始。&lt;br /&gt;
=== 添加Bukkit API ===&lt;br /&gt;
&lt;br /&gt;
在开发插件之前，你需要添加 Bukkit API库文件到你的项目，作为一个dependency, 你也可以添加其他你想实用的API.&lt;br /&gt;
&lt;br /&gt;
找到项目文件夹中部的&#039;&#039;pom.xml&#039;&#039;并双击进行编辑. 点击&#039;&#039;pom.xml&#039;&#039;中部的 tab,你将会看到下图所示内容:&lt;br /&gt;
&lt;br /&gt;
[[Image:Pomeditor.png]]&lt;br /&gt;
&lt;br /&gt;
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;build&amp;gt;&lt;br /&gt;
       &amp;lt;plugins&amp;gt;&lt;br /&gt;
           &amp;lt;plugin&amp;gt;&lt;br /&gt;
               &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;
               &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
               &amp;lt;configuration&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;amp;lt;&amp;lt;/nowiki&amp;gt;source&amp;gt;1.7&amp;lt;/source&amp;gt;&lt;br /&gt;
                   &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;&lt;br /&gt;
               &amp;lt;/configuration&amp;gt;&lt;br /&gt;
           &amp;lt;/plugin&amp;gt;&lt;br /&gt;
       &amp;lt;/plugins&amp;gt;&lt;br /&gt;
    &amp;lt;/build&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你或许想使用其他版本,例如 &amp;lt;code&amp;gt;1.8&amp;lt;/code&amp;gt; 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码&amp;quot;error&amp;quot;中建议你更改语言版本.同意即可.&lt;br /&gt;
&lt;br /&gt;
在位于代码中段的&#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit&#039;s repository的地址): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;repositories&amp;gt;&lt;br /&gt;
        &amp;lt;repository&amp;gt;&lt;br /&gt;
            &amp;lt;id&amp;gt;bukkit-repo&amp;lt;/id&amp;gt;&lt;br /&gt;
            &amp;lt;url&amp;gt;http://repo.bukkit.org/content/groups/public/&amp;lt;/url&amp;gt;&lt;br /&gt;
        &amp;lt;/repository&amp;gt;&lt;br /&gt;
    &amp;lt;/repositories&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后继续在 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;dependencies&amp;gt;&lt;br /&gt;
        &amp;lt;dependency&amp;gt;&lt;br /&gt;
            &amp;lt;groupId&amp;gt;org.bukkit&amp;lt;/groupId&amp;gt;&lt;br /&gt;
            &amp;lt;artifactId&amp;gt;bukkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
            &amp;lt;version&amp;gt;1.7.2-R0.3&amp;lt;/version&amp;gt;&lt;br /&gt;
            &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;
            &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;
        &amp;lt;/dependency&amp;gt;&lt;br /&gt;
    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.&lt;br /&gt;
&lt;br /&gt;
当你完成上述步骤后,&#039;&#039;pom.xml&#039;&#039;中的内容应该是这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Finishedpom.png]]&lt;br /&gt;
&lt;br /&gt;
通过菜单 &#039;&#039;File -&amp;gt; Save&#039;&#039;或者按住 &amp;lt;code&amp;gt;Ctrl + S&amp;lt;/code&amp;gt; 来保存&#039;&#039;pom.xml&#039;&#039;文件 .  之后, 右键项目标题，然后依次选择 &#039;&#039;Maven -&amp;gt; Update Project&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Bukkit Java文档 ===&lt;br /&gt;
&lt;br /&gt;
 如果你曾经使用过Eclipse和Java，你会知道当你将鼠标准心移至class或者 method中的代码部分时，一个含有相关文档内容的黄色小框会弹出来. 这就是（Java的）Java文档。&lt;br /&gt;
 &lt;br /&gt;
 你可以[http://download.oracle.com/javase/6/docs/api/ Oracle website 在线获取]它.。Bukkit同样拥有文档，这些文档内容包含了关于API中method和class的有用描述。&lt;br /&gt;
 &lt;br /&gt;
 你可以在[http://jd.bukkit.org/ 这]找到它 (Beta版的Java文档在[http://jd.bukkit.org/ 这]找, 新开发出来的Java文档在  &lt;br /&gt;
 &lt;br /&gt;
 [http://jd.bukkit.org/ 这]) 。为了在Eclipse里使用Java文档,这样的话当你的鼠标停留在Bukkit的class和method上时，&lt;br /&gt;
 &lt;br /&gt;
 （Bukkit的）Java文档就会冒出来。  &lt;br /&gt;
JavaDoc in spigot [https://hub.spigotmc.org/javadocs/bukkit/ Click Here]&lt;br /&gt;
&lt;br /&gt;
 首先右键在侧边栏里的&amp;quot;Maven Dependencies&amp;quot;下面的Bukkit jar,选择&amp;quot;Properties&amp;quot;.选中窗口左边的&#039;&#039;Javadoc Location&#039;&#039;, 并粘贴如下网址 &lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;http://jd.bukkit.org/&#039;&#039;&#039; (或者上面的测试版/最新版 Java文档链接也好) 到&amp;quot;Javadoc URL&amp;quot;下面的输入框内. 就像这样: &lt;br /&gt;
&lt;br /&gt;
[[Image:Bukkitjavadocs.png]] &lt;br /&gt;
&lt;br /&gt;
 点击 &amp;quot;Validate&amp;quot;,然后再点击&amp;quot;OK&amp;quot;就完成了.现在Bukkit Javadocs已经连接到Bukkit提供的资源,同时你也可以通过Eclipse来获得帮助类文档信息了.&lt;br /&gt;
&lt;br /&gt;
=== 创建一个包 ===&lt;br /&gt;
&lt;br /&gt;
 现在你需要创建一个包，它将储存所有我们需要使用的Java类文件. 右键展开&#039;&#039;src/main/java&#039;&#039;折叠并选择 &#039;&#039;New &amp;amp;gt; Package&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
[[Image:Newpackage.png]] &lt;br /&gt;
&lt;br /&gt;
 包名的话，先写上你的组名，然后加个点，再写上你的小写Artifact ID。举例, 如果你的组名是&#039;&#039;io.github.name&#039;&#039;，你的artifact名是 &#039;&#039;TestPlugin&#039;&#039;。&lt;br /&gt;
 &lt;br /&gt;
 你的包名就应该是&#039;&#039;io.github.name.testplugin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== 创建插件的类文件 ===&lt;br /&gt;
&lt;br /&gt;
既然我们已经建立好了我们的项目，我们接下来可以开始添加类文件以及制作插件了. 插件的主类指的是拓展(extends)JavaPlugin的类文件. 在你的插件中只能有一个类文件拓展JavaPlugin，无论直接还是间接. 先创建你的主类文件并保持名称与插件名一致是个很好的习惯.右键你之前新建的包,并选择&amp;amp;nbsp; &#039;&#039;New &amp;amp;gt; Class&#039;&#039;.你将会创建一个新的类文件，就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$GroupName}.{$ArtifactName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$ArtifactName} extends JavaPlugin {&lt;br /&gt;
    &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
警告： 插件绝对不应该调用自己的构造函数并实例化&lt;br /&gt;
&lt;br /&gt;
=== 创建plugin.yml ===&lt;br /&gt;
&lt;br /&gt;
你已经创建了项目和主类，如果你想要Bukkit能够加载你的插件，你还必须创建 &#039;&#039;&#039;[[Plugin YAML|plugin.yml]]&#039;&#039;&#039; 文件. 这个文件包含有基础的插件信息，如果缺失这个文件，你的插件也将会失效. 此时你需要右键&#039;&#039;src/main/resources&#039;&#039;. 选择&#039;&#039;New &amp;amp;gt; File&#039;&#039;. 命名为&amp;quot;&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;&amp;quot; 并右键完成新文件的创建.Eclipse会打开默认的文本编辑窗口来显示&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;文件中空白的内容并提供编辑. &#039;&#039;(Hint:&amp;amp;nbsp;如果你想要使得你的工作空间更加规整，关闭文本编辑器并将&#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件拖到主工作空间(拖到文件目录的右边) 之后你就可以在eclipse中编辑文件了.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
有三个基础的内容需要在plugin.yml写明. &lt;br /&gt;
: name:  插件名称. &lt;br /&gt;
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 插件主类的完整，合法名称] . &lt;br /&gt;
: version: 插件当前版本号. &lt;br /&gt;
&lt;br /&gt;
最简单的 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件内就像这样&amp;amp;nbsp;: &lt;br /&gt;
&lt;br /&gt;
 name: {插件名称}&lt;br /&gt;
 main: {包名}.{主类}&lt;br /&gt;
 version: {版本号}&lt;br /&gt;
&lt;br /&gt;
 PS:插件的包名经常会包括插件的名称，所以看到这个的时候不要感到惊讶。&lt;br /&gt;
    你的主类是否是你的插件名取决于你之前的命名方式，时刻记住这一点很重要。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;更多例子&#039;&#039;&#039;, 请看[[#Example_Files_and_Templates]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
此时你的插件已经可以被Bukkit加载了,同时服务端日志也会开始记录你的插件. 不过它现在什么用都没有!&lt;br /&gt;
&lt;br /&gt;
== onEnable()和onDisable()方法  ==&lt;br /&gt;
&lt;br /&gt;
这些方法将在插件启用与卸载时生效. 默认情况下，你的插件在被加载时会调用这些方法，所以你可以在这里注册你需要用到的事件和提供一些调试信息. &amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 方法会在插件启用时被调用, 需要包含初始化插件的逻辑语句. &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法会在插件卸载时被调用，需要包含清理(clean up)插件的逻辑语句和连接声明(associated state)。此外，插件可以重写 &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; 方法来在插件载入时执行附加的逻辑语句。&lt;br /&gt;
&lt;br /&gt;
=== onEnable()和onDisable()方法介绍  ===&lt;br /&gt;
&lt;br /&gt;
在前面的章节中，我们在主类创建了&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法. 此时，这些代码看起来就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
     @Override&lt;br /&gt;
     public void onEnable() {&lt;br /&gt;
         // 插件载入时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     @Override&lt;br /&gt;
     public void onDisable() {&lt;br /&gt;
         // 插件卸载时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这些方法已经创建，但目前它们还没有任何作用.&lt;br /&gt;
注意: 不需要添加代码来实现输出信息&amp;quot;{$PluginName} has been enabled!&amp;quot; ，因为bukkit会自动输出此类信息&lt;br /&gt;
有关更多事件的信息请查看 [[Event_API_Reference|这里]].&lt;br /&gt;
&lt;br /&gt;
=== 发送提示信息 ===&lt;br /&gt;
&lt;br /&gt;
 插件能够输出信息至控制台与服务器日志。这是通过调用插件记录器里正确的方法来实现的。首先我们必须调用 &amp;lt;code&amp;gt;getLogger()&amp;lt;/code&amp;gt; 方法来检索该插件的记录器，&lt;br /&gt;
 &lt;br /&gt;
 然后我们就可以开始记录信息了。&lt;br /&gt;
&lt;br /&gt;
我们通过调用&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法可以实现输出信息。 我们需要在&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法下面插入如下代码：&lt;br /&gt;
&lt;br /&gt;
 getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
在 &amp;lt;code&amp;gt;&#039;&#039;&#039;onDisable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法里面，你也可以做类似的事情，不过要改改输出信息。&lt;br /&gt;
&lt;br /&gt;
此时你的主类文件就像这样: &lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
         @Override&lt;br /&gt;
         public void onEnable() {&lt;br /&gt;
             getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public void onDisable() {&lt;br /&gt;
         getLogger().info(&amp;quot;onDisable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 防止重载后插件失效 ===&lt;br /&gt;
&lt;br /&gt;
你需要知道的是，插件并不只会伴随服务器关闭、启动而重载，你的插件也可能会被其他插件启用、关闭，或是在服务器运行时使用/reload命令. &lt;br /&gt;
假设服务器启动之后一会儿后，你的插件才被启用，这时便是危险的,因为服务器中可能已经有玩家在线、额外的世界被加载以及很多预想不到的不同之处(与&amp;quot;插件同服务器同时加载&amp;quot;的情况相比较). &lt;br /&gt;
&lt;br /&gt;
举个例子来说:&lt;br /&gt;
* 你有一个用来储存HashMap中的玩家登入信息的插件&lt;br /&gt;
* 你希望该信息对所有玩家都可见&lt;br /&gt;
* 一名管理员使用/reload命令&lt;br /&gt;
* 你的插件被卸载，所有储存的数据都丢失了&lt;br /&gt;
* 你的插件被再次启用时，一些玩家已经在线&lt;br /&gt;
* 这些玩家在HashMap并没有储存的信息&lt;br /&gt;
* 你试图重新获得他们的信息，但是失败了!&lt;br /&gt;
&lt;br /&gt;
为了插件重载以后能够正常工作, 你需要在插件启用时加载所有已经在线的玩家的信息并将之储存在HashMap中.&lt;br /&gt;
&lt;br /&gt;
 for (Player player : Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
     playerList.put(player.getName(), playerData(player));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== 监听器  ==&lt;br /&gt;
监听器是一种方法被调用来对事件作出反应的类.所有的监听器使用 org.bukkit.event.Listener接口.更多有关监听器创建的细节,&lt;br /&gt;
:: &#039;&#039; 请查看: [[Event API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 指令  ==&lt;br /&gt;
&lt;br /&gt;
=== onCommand() 方法  ===&lt;br /&gt;
&lt;br /&gt;
 现在你已经知道如何注册一个事件并做出响应,但是如果你只是想要在命令输入之后做出响应呢? 你可以使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 这个代码的作用是当玩家输入“/”时，监听该操作并执行相关语句&amp;quot;/&amp;quot; . 举个例子来说.输入 &amp;quot;/do something&amp;quot; 将会执行 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 此种情况下，因为没有特定的行为被编程，所以并不会发生任何事情。&lt;br /&gt;
&lt;br /&gt;
 请避免使用和bukkit所提供的指令重名的指令, 然后深思你的指令名的唯一性。 &lt;br /&gt;
 &lt;br /&gt;
 例如.指令&amp;quot;give&amp;quot;已经被好几个插件使用了, 如果你执行了另外一个&amp;quot;give&amp;quot;指令, 你的插件将会和这些插件冲突。&lt;br /&gt;
&lt;br /&gt;
 你必须在插件的&amp;quot;plugins.yml&amp;quot;注册你的指令 否则这个方法将不会被触发。&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法必须返回一个布尔值（true或false）。 &lt;br /&gt;
 &lt;br /&gt;
 如果返回值是true，你不会看到什么明显的事情发生。 &lt;br /&gt;
 &lt;br /&gt;
 但如果返回值是false，则会返回你的plugin.yml里的&#039;usage:property&#039;然后发送给命令使用者. &lt;br /&gt;
&lt;br /&gt;
当使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时, 你需要填写4个参数. &lt;br /&gt;
&lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 发送命令的对象&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;Command cmd&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行的指令&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String commandLabel&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行指令的别名&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String[] args&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 该指令的自变量数组。&lt;br /&gt;
 &lt;br /&gt;
例如.指令 &#039;&#039;/hello abc def&#039;&#039; 中， &#039;&#039;abc&#039;&#039; 会被存放进args[0]中, &#039;&#039;def&#039;&#039; 被存放进args[1]中。&lt;br /&gt;
&lt;br /&gt;
==== 设置命令  ====&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { // 如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
     // 所需要执行的事（此处略）&lt;br /&gt;
         return true;&lt;br /&gt;
     } //如果以上内容成功执行，则返回true &lt;br /&gt;
     // 如果执行失败，则返回false.&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 每当使用&amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时， 在每个方法后面都加上一个 return false; 是一个惯例。 &lt;br /&gt;
 &lt;br /&gt;
 返回false会显示在plugin.yml里设置的usage信息 (看下面). 用这种方法的话，一旦执行出错便会显示usage信息。&lt;br /&gt;
&lt;br /&gt;
 每当方法return一个值的时候，这个方法就会退出，所以return true的时候，在它下面的代码就会被跳过, &lt;br /&gt;
 &lt;br /&gt;
 除非return语句在一个if的嵌套中，或者类似的嵌套情况。 &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;.equalsIgnoreCase(&amp;quot;basic&amp;quot;)&#039;&#039;&#039;&amp;lt;/code&amp;gt;代表忽略英文大小写. 在这种情况下,字符串&amp;quot;BAsIc&amp;quot; 和 &amp;quot;BasiC&amp;quot;和 basic相同，代码会被照常执行。&lt;br /&gt;
&lt;br /&gt;
和以前一样，加两行import在你的java文件头&lt;br /&gt;
&lt;br /&gt;
 import org.bukkit.command.Command;&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.command.CommandSender;&lt;br /&gt;
&lt;br /&gt;
=== 在Plugin.yml中添加你的指令  ===&lt;br /&gt;
&lt;br /&gt;
你需要添加你的指令到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件里. 如下是指令/basic添加到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039;的例子（请在plugins.yml的末尾添加如下代码）: &lt;br /&gt;
&lt;br /&gt;
commands:&lt;br /&gt;
   basic:&lt;br /&gt;
      description: This is a demo command.&lt;br /&gt;
      usage: /&amp;lt;command&amp;gt; [player]&lt;br /&gt;
      permission: &amp;lt;plugin name&amp;gt;.basic&lt;br /&gt;
      permission-message: You don&#039;t have &amp;lt;permission&amp;gt;&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;basic&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令名称&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;description&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令描述&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;usage&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 在oncommand方法return false后显示的用法提示。尽量简洁, 使别人能够理解指令是什么以及如何使用它。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被一些显示帮助信息的插件使用，用来决定向玩家显示哪个指令（完全不对劲吧！）。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission-message&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 当玩家使用了这个指令而没有权限时输出的信息。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 PS:yml文件使用两个空格作为制表符,使用tab键输入制表符会导致错误。&lt;br /&gt;
&lt;br /&gt;
===控制台指令vs玩家指令===&lt;br /&gt;
&lt;br /&gt;
 你可能注意到了上文的&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt;参数. &amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt; 是个Bukkit接口，&lt;br /&gt;
 &lt;br /&gt;
 它有两个（对插件编写者）有用的子类：&amp;lt;code&amp;gt;&#039;&#039;&#039;Player&#039;&#039;&#039;&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;&#039;&#039;&#039;ConsoleCommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 所以当你编写插件的时候, 确定这个指令从控制台发出后能完全正常工作是十分必要的,&lt;br /&gt;
 &lt;br /&gt;
 那些只能由在线玩家执行的指令 &#039;&#039;只能&#039;&#039; 由一个在线玩家执行。&lt;br /&gt;
 &lt;br /&gt;
 有的插件在判断命令执 行对象不是玩家时仅仅用return进行处理 (比如当有人从控制台发出指令), &lt;br /&gt;
 &lt;br /&gt;
 即便这些指令能够在控制台完美运行（比如改变天气的指令）。&lt;br /&gt;
&lt;br /&gt;
一个判断sender的方法: &lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { //如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
             // 所需要执行的事（此处略）&lt;br /&gt;
             return true;&lt;br /&gt;
         } else if (cmd.getName().equalsIgnoreCase(&amp;quot;basic2&amp;quot;)) {&lt;br /&gt;
             if (!(sender instanceof Player)) { //如果sender与Player类不匹配&lt;br /&gt;
                 sender.sendMessage(&amp;quot;This command can only be run by a player.&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 Player player = (Player) sender;&lt;br /&gt;
                    // 所需要执行的事（此处略）&lt;br /&gt;
             }&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 在这个例子里，指令&#039;&#039;&#039;basic&#039;&#039;&#039;能由任何人发出 - 一个在线的玩家, 或者服务器后台的op. &lt;br /&gt;
 &lt;br /&gt;
 但是指令&#039;&#039;&#039;basic2&#039;&#039;&#039;只能由一个在线的玩家发出. &lt;br /&gt;
&lt;br /&gt;
 大体上说,你应该允许尽可能多的指令在控制台和玩家聊天框里都正常执行. &lt;br /&gt;
 &lt;br /&gt;
 需要在线玩家执行的指令可以使用以上例子中的机制来检测&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;是一个玩家. &lt;br /&gt;
 &lt;br /&gt;
 很多指令广泛依靠于一些玩家的属性（例如可以被tp或被给予物品等等）。&lt;br /&gt;
&lt;br /&gt;
 如果你想更进一步, 你可以对你的指令的自变量做一些额外的检测， 例如传送指令在提供玩家ID的情况下（也只有在这种情况下）可以在控制台被执行。&lt;br /&gt;
&lt;br /&gt;
=== 使用独立的 CommandExecutor class  ===&lt;br /&gt;
&lt;br /&gt;
 以上的例子必须把 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法放在插件的主类里。&lt;br /&gt;
 &lt;br /&gt;
 对于小插件来说，这是极好的,但如果你要写一个大点的插件,把你的 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法放在独立的类里将会很有意义。&lt;br /&gt;
 &lt;br /&gt;
 幸运的是，这并不难: &lt;br /&gt;
&lt;br /&gt;
*在你的包里创建一个新类. 命名为&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;之类的 (当然，要把&#039;&#039;&#039;MyPlugin&#039;&#039;&#039;替换成你制作的插件的名字)。 这个类 &#039;&#039;&#039;必须&#039;&#039;&#039; 继承Bukkit 的&#039;&#039;&#039;CommandExecutor&#039;&#039;&#039;接口。&lt;br /&gt;
&lt;br /&gt;
*在你的插件的&amp;lt;code&amp;gt;&#039;&#039;&#039;onEnable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法中,你需要实例化你创建的命令执行类(CommandExecutor class), 然后做一个像下面一样的调用：&amp;lt;code&amp;gt;getCommand(&amp;quot;basic&amp;quot;).setExecutor(myExecutor);&amp;lt;/code&amp;gt;&lt;br /&gt;
 其中&amp;quot;basic&amp;quot;是我们需要操控的指令, &amp;lt;code&amp;gt;myExecutor&amp;lt;/code&amp;gt; 是我们创建的实例.&lt;br /&gt;
&lt;br /&gt;
废话少说直接吔我例子: &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyPlugin.java&#039;&#039;&#039; (插件的主类): &lt;br /&gt;
 @Override&lt;br /&gt;
 public void onEnable() {&lt;br /&gt;
         // 如果你没有在plugins.yml注册过指令的话，此处会抛出空指针异常！&lt;br /&gt;
         this.getCommand(&amp;quot;basic&amp;quot;).setExecutor(new MyPluginCommandExecutor(this));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;MyPluginCommandExecutor.java&#039;&#039;&#039;（独立CommandExecutor类）: &lt;br /&gt;
 public class MyPluginCommandExecutor implements CommandExecutor {&lt;br /&gt;
         private final MyPlugin plugin;&lt;br /&gt;
 &lt;br /&gt;
         public MyPluginCommandExecutor(MyPlugin plugin) {&lt;br /&gt;
             this.plugin = plugin; // Store the plugin in situations where you need it.&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
             // 和以前一样执行就好…&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 请注意我们是如何把主类里的对象指向&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;的. 这允许我们轻易的使用主类对象的方法（如果我们需要的话）。&lt;br /&gt;
&lt;br /&gt;
 这么做的话, 我们便能更好的组织代码 - 如果 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法又大又复杂, 它就能被划分为子类，从而不使插件的主类显得凌乱。&lt;br /&gt;
&lt;br /&gt;
 PS：如果你的插件含有多个指令的话, 你需要为每一个指令单独设立一个commandexecutor.&lt;br /&gt;
&lt;br /&gt;
=== 写一个安全的指令 ===&lt;br /&gt;
 当写一个指令的时候,别进行任何假设（很重要）, 例如假设执行者一定是个玩家. 请牢记以下原则:&lt;br /&gt;
&lt;br /&gt;
==== 在命令执行前确定发出者是玩家 ====&lt;br /&gt;
&lt;br /&gt;
使用简单的例子来进行检测:&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (sender instanceof Player) {&lt;br /&gt;
             Player player = (Player) sender;&lt;br /&gt;
             // do something&lt;br /&gt;
         } else {&lt;br /&gt;
             sender.sendMessage(&amp;quot;You must be a player!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
             // do something&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 检测自变量个数 ====&lt;br /&gt;
&lt;br /&gt;
 别老是认为玩家都能打对正确数量的自变量. &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (args.length &amp;gt; 4) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Too many arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         } &lt;br /&gt;
         if (args.length &amp;lt; 2) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Not enough arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 在获取玩家之前先检测他们是否在线 ====&lt;br /&gt;
&lt;br /&gt;
 有时你想通过命令发出者打出的ID来获取其他玩家，请确保他们在线再说！&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         Player target = (Bukkit.getServer().getPlayer(args[0]));&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not online!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 如果你想对离线玩家进行操作，&amp;lt;code&amp;gt;&#039;&#039;&#039;OfflinePlayer&#039;&#039;&#039;&amp;lt;/code&amp;gt;类提供了基础操作的方法.&lt;br /&gt;
&lt;br /&gt;
== 插件的配置/设置  ==&lt;br /&gt;
Bukkit API 为插件提供了一种简便的方法，用来管理用户自定义的设置。同时，通过这种方法也可以储存数据。&lt;br /&gt;
:: &#039;&#039;详情请查看: [[Configuration API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 权限  ==&lt;br /&gt;
&lt;br /&gt;
利用新的Bukkit API来设置权限是再容易不过的事情. 检测玩家是否拥有权限，可以使用以下代码: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;if (player.hasPermission(&amp;quot;some.pointless.permission&amp;quot;)) {&lt;br /&gt;
   //执行此段代码(有权限)&lt;br /&gt;
} else {&lt;br /&gt;
   //执行此段代码(无权限)&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你也可以检测一个权限节点是否被定义过，(等效于 Java 语言中的&#039;&#039;&#039;null&#039;&#039;&#039;)，使用下面的方法: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;boolean isPermissionSet(String name)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你或许对此抱有疑惑:为什么这段代码中没有任何用户群的信息？因为它们并不是必需的. 之前这段代码想要实现的主要功能之一就是根据用户群格式化聊天信息. 但其实使用权限就可以很容易地完成这项工作.你可以在聊天插件的配置文件中定义权限与前缀的对应关系. 举例来说，&amp;quot;someChat.prefix.admin&amp;quot; 权限可以对应前缀[Admin].当拥有此权限的玩家发出聊天信息时 ，聊天前缀便是[Admin]. &lt;br /&gt;
&lt;br /&gt;
另一个想要实现的主要功能则是给所有拥有权限的在线用户发一条信息.你可以这样做: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;for (Player player: Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
    if (player.hasPermission(&amp;quot;send.receive.message&amp;quot;)) {&lt;br /&gt;
        player.sendMessage(&amp;quot;You were sent a message&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
看到这里你可能会问，若是没有权限组我该如何配置玩家的 [[权限]] ？既然 [[Bukkit]] API 本身并没有提供用户组的预设, 你必须通过安装一款权限组插件，像 [[permissionsBukkit]]  这样的插件来帮助你配置用户群. &#039;&#039;&#039;API 只是提供接口而不是提供implementation.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
=== 配置权限 ===&lt;br /&gt;
{{Hide|标题=译者注 |内容= 其一，以下代码中部分内容将以&#039;&#039;//文字&#039;&#039;翻译，使用时请不要加入其中内容，仅做教程中的翻译参考；&lt;br /&gt;
其二，default:true在下面翻译为全员默认拥有该权限，default:false则翻译为全员默认没有该权限，请注意理解。 }}&lt;br /&gt;
如果你想要更多地控制你的权限，例如设置权限的默认值或是子权限节点，不如将权限节点加入你的 &#039;&#039;[[plugin.yml]]&#039;&#039;文件.这并不是必须的，但我们推荐你这样做. 下面是一个实例，显示了当你将权限添加在&#039;&#039;plugin.yml&#039;&#039;文件末尾时的样子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands //给予使用doorman所有命令的权限&lt;br /&gt;
        children:  //子权限节点&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&lt;br /&gt;
    doorman.kick:&lt;br /&gt;
        description: Allows you to kick a user  //允许你踢出一名用户&lt;br /&gt;
        default: op  //OP默认拥有该权限&lt;br /&gt;
    doorman.ban:&lt;br /&gt;
        description: Allows you to ban a user  //允许你封禁一名用户&lt;br /&gt;
        default: op //OP默认拥有该权限&lt;br /&gt;
    doorman.knock:&lt;br /&gt;
        description: Knocks on the door! //敲门&lt;br /&gt;
        default: true //全员默认拥有该权限&lt;br /&gt;
    doorman.denied:&lt;br /&gt;
        description: Prevents this user from entering the door  //禁止拥有该权限节点的用户进入门内&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
值得注意的是,你的插件使用的每个权限都被定义为 &#039;&#039;permissions&#039;&#039; 这个节点的子权限节点. 每个权限都能够拥有一个描述，默认值以及子权限节点。&lt;br /&gt;
==== 默认值  ====&lt;br /&gt;
&lt;br /&gt;
当一个权限没有明确是否为玩家所拥有&amp;amp;nbsp;，&#039;&#039;hasPermission&#039;&#039;&amp;lt;ref&amp;gt;这是一个检测玩家是否拥有指定权限节点的方法&amp;lt;/ref&amp;gt;&amp;amp;nbsp;将返回false&amp;lt;ref&amp;gt;该方法返回的boolean值&amp;lt;/ref&amp;gt;. 在你的[[plugin.yml]] you can change this by setting the default node to be one of four values: &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;true&#039;&#039;&#039; - 全员默认拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;false&#039;&#039;&#039; - 全员默认没有该权限. &lt;br /&gt;
*&#039;&#039;&#039;op&#039;&#039;&#039; - 如果玩家是一名OP&amp;lt;ref&amp;gt;Operator，服务器管理员&amp;lt;/ref&amp;gt;，那么他将拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;not op&#039;&#039;&#039; - 如果玩家不是一名OP，那么他将拥有该权限.&lt;br /&gt;
&lt;br /&gt;
==== 子权限  ====&lt;br /&gt;
&lt;br /&gt;
在这之前你可能已经习惯了通过权限节点自动分配所有的子权限&amp;lt;ref&amp;gt;原句:Before now you will probably be used to the * permission to automatically assign all sub permissions.此处的sub permissions应是同上文“你的插件使用的每个权限都被定义为 permissions 这个节点的子权限节点.”相照应&amp;lt;/ref&amp;gt;. 现在这种方法已经随着bukkit API&amp;lt;ref&amp;gt;应用程序编程接口(Application Programming Interface)&amp;lt;/ref&amp;gt;的更新而改变，现在你可以自己定义子权限了. 这将使得插件具有更多的灵活性. 下面是一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands  //翻译见上&lt;br /&gt;
        children:&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
在这里，&#039;&#039;doorman.*&#039;&#039; 权限拥有几个子权限并分配在其下. 子权限工作的方式就是当&#039;&#039;doorman.*&#039;&#039; 权限节点被设置为true时,子权限也会随之设置为 &#039;&#039;plugin.yml&#039;&#039;中的默认值. 然而当&#039;&#039;doorman.*&#039;&#039; 被设置为false时，所有子权限都会设置为与默认值相反的值（true-&amp;gt;false,false-&amp;gt;true）.&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设置你自己的权限  ===&lt;br /&gt;
&lt;br /&gt;
如果你希望想知道更多有关开发你自己的权限插件的事情(即是设置权限的管理类插件) ，那么请前往查看教程&amp;amp;nbsp;[[Developing a permissions plugin]].&lt;br /&gt;
&lt;br /&gt;
== Scheduling Tasks and Background Tasks  ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 方块操控&amp;lt;br&amp;gt;  ==&lt;br /&gt;
&lt;br /&gt;
这是一个很容易的方法创建、获取或修改一个方块, 例如当你想要修改往你头上数第五格的方块时, 首先你需要做的是获取你当前的坐标点, 然后往你当前的坐标点的Y轴加上五, 获取目标方块后我们便能够很容易地修改它了, 例子如下:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@EventHandler&lt;br /&gt;
public void onPlayerMove(PlayerMoveEvent event) {&lt;br /&gt;
    // 得到玩家当前的坐标.&lt;br /&gt;
    Location loc = event.getPlayer().getLocation();&lt;br /&gt;
    // 设置目标坐标为玩家头顶 5 格处.&lt;br /&gt;
    loc.setY(loc.getY() + 5);&lt;br /&gt;
    // 得到目标坐标的方块&lt;br /&gt;
    Block b = loc.getBlock();&lt;br /&gt;
    // 将目标坐标的方块更改为石头 (stone).&lt;br /&gt;
    b.setType(Material.STONE);&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
上面的代码获取了玩家的所在位置，获取头上的第五个方块，并且放上一个石头（stone）。 Note that once you have a &amp;lt;code&amp;gt;Block&amp;lt;/code&amp;gt;, there are other things you can do besides set its type. 你可以查阅JavaDoc获取更多的东西。&lt;br /&gt;
&lt;br /&gt;
你可以用这样相似的观念去生成一些建筑，并且还可以用编程的方式通过算法生成一些独特的方块（这里指由很多个方块拼在一起的四四方方的方块）。比如，去生成一个实心的方块建筑，你可能在生成建筑的时候还需要用到&amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;循环，去一遍遍循环来生成这些建筑，填满这些建筑等。&lt;br /&gt;
下面，是一个在指定坐标生成一个实心的方块建筑的实例方法：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void generateCube(Location loc, int length) {&lt;br /&gt;
    // 放置这个方块最中间的方块并且获取这个方块的坐标。&lt;br /&gt;
    // Uses getBlockN() instead of getN() to avoid casting to an int later.&lt;br /&gt;
    int x1 = loc.getBlockX(); &lt;br /&gt;
    int y1 = loc.getBlockY();&lt;br /&gt;
    int z1 = loc.getBlockZ();&lt;br /&gt;
&lt;br /&gt;
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.&lt;br /&gt;
    int x2 = x1 + length;&lt;br /&gt;
    int y2 = y1 + length;&lt;br /&gt;
    int z2 = z1 + length;&lt;br /&gt;
&lt;br /&gt;
    World world = loc.getWorld();&lt;br /&gt;
&lt;br /&gt;
    // Loop over the cube in the x dimension.&lt;br /&gt;
    for (int xPoint = x1; xPoint &amp;lt;= x2; xPoint++) { &lt;br /&gt;
        // Loop over the cube in the y dimension.&lt;br /&gt;
        for (int yPoint = y1; yPoint &amp;lt;= y2; yPoint++) {&lt;br /&gt;
            // Loop over the cube in the z dimension.&lt;br /&gt;
            for (int zPoint = z1; zPoint &amp;lt;= z2; zPoint++) {&lt;br /&gt;
                // Get the block that we are currently looping over.&lt;br /&gt;
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);&lt;br /&gt;
                // 设置方块ID为57号(钻石块!)&lt;br /&gt;
                currentBlock.setType(Material.DIAMOND_BLOCK); //这里下面会提到&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
这个方法将会在程序给予这个方法高度和起始坐标（最中间的方块的坐标）时，构建一个3D的方块建筑。简单的删除方块也可以使用这个方法的思路实现，但是，但是设置方块的时候，记得把上面标注会提到的那行，设置为物品ID为0的方块(空气方块)。&amp;lt;br&amp;gt; &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== (玩家)操控背包  ==&lt;br /&gt;
&lt;br /&gt;
这个部分大部分讲的是操控玩家的背包。但是在某些情况下，操控箱子里的内容与此相同，当然得在你能找到获取箱子里内容的方法才可以。&amp;amp;nbsp;:P. 这是一个操控玩家背包的一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void onPlayerJoin(PlayerJoinEvent evt) {&lt;br /&gt;
    Player player = evt.getPlayer(); // 当玩家加入游戏&lt;br /&gt;
    PlayerInventory inventory = player.getInventory(); // 获取玩家背包列表&lt;br /&gt;
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // 生成一组钻石&lt;br /&gt;
        &lt;br /&gt;
    if (inventory.contains(itemstack)) {&lt;br /&gt;
        inventory.addItem(itemstack); // 将一组钻石放到玩家的背包里&lt;br /&gt;
        player.sendMessage(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;); //向玩家发送消息(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
所以，上面代码里面的 onPlayerJoin 方法中，我们首先构建了一些变量，让我们的工作变得更容易一些。变量分别是： player, inventory and itemstack。 inventory 变量是玩家的背包内容，itemstack是那64个钻石的物品栈。然后我们判断了玩家是不是有一组钻石。如果有，我们就用inventory.addItem(itemstack)方法再给玩家一组钻石，并且给他发送一个消息“Wow！你看上去很土豪啊！”。 所以操控背包并不像我们想象的那么难，如果我们想实现让玩家进入游戏，检测到一组钻石后删掉这组钻石，只需要把代码里的inventory.addItem(itemstack)替换成inventory.remove(itemstack)。获取你还得把“Wow！你看上去很土豪啊！”改成“Wow！你看上去好穷啊！”2333333。&lt;br /&gt;
&lt;br /&gt;
== Item Manipulation ==&lt;br /&gt;
&lt;br /&gt;
When dealing with items in the code, you use the ItemStack class for looking up and setting all information on that stack.&lt;br /&gt;
&lt;br /&gt;
=== Enchantments ===&lt;br /&gt;
&lt;br /&gt;
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&#039;t work) because they&#039;re abstract, so you must use an EnchantmentWrapper. If you want to enchant items that can&#039;t be enchanted inside normal SMP, use addUnsafeEnchantment() instead of addEnchantment()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
int itemCode = 280;  //use the item code you want here&lt;br /&gt;
int effectId = 20;  //use the enchantment code you want here&lt;br /&gt;
int enchantmentLevel = 100;&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(itemCode);  //new item of item code&lt;br /&gt;
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id&lt;br /&gt;
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //enchant the item&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ItemMeta ===&lt;br /&gt;
You can set the display name of an item by doing this.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
String myDisplayName = &amp;quot;Awesome Sword&amp;quot;; //这里是保存了你想要显示的名字&lt;br /&gt;
 &lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你要更改的方块&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //得到方块的元数据&lt;br /&gt;
im.setDisplayName(myDisplayName); //设置它显示的名字&lt;br /&gt;
myItem.setItemMeta(im); //保存更改&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
You can also set the lores of an item. The lores are the small annotations on an item, like &amp;quot;+5 attack damage&amp;quot; on a stone sword.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;String&amp;gt; lores = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
lores.add(&amp;quot;Example lore&amp;quot;, &amp;quot;this one comes on line 2&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again&lt;br /&gt;
im.setLore(lores); //add the lores of course&lt;br /&gt;
myItem.setItemMeta(im); //give the item the new itemmeta&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maps, and Sets, and Lists, Oh My! &amp;lt;ref&amp;gt;标题中Map,Set,List均为Java类集中的接口，故不翻译&amp;lt;/ref&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
除了Map类和HashMap类，Java中还提供了其它的数据结构类集。 &lt;br /&gt;
&lt;br /&gt;
他们提供这些不同的类，是因为有些时候，使用Map类并不合适。&amp;lt;nowiki&amp;gt;[[Java data structure classes]]&amp;lt;/nowiki&amp;gt;在这个条目中，有对Java数据框架类集更深入的讲述。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;=== HashMaps概览以及如何使用此接口  ===&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;Keep in mind to never use a player in a hashmap!&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;&amp;lt;ref&amp;gt;下面有讲解。&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
应在HashMap中使用字符串.，使用 &amp;quot;p.getName()&amp;quot; 来添加，移除或是检查列表中是否含有某个玩家.&lt;br /&gt;
&lt;br /&gt;
将玩家当作object储存在HashMap中会造成大量内存泄漏.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;adam&amp;quot; as the key and assigned a value of &amp;quot;a&amp;quot; to it. That would work as intended, but then say afterwards I wanted to assign the value of &amp;quot;b&amp;quot; to key &amp;quot;adam&amp;quot; I would be able to and would get no errors but the value of &amp;quot;a&amp;quot; assigned to key &amp;quot;adam&amp;quot; in the HashMap would be overwritten because HashMaps cannot contain duplicate values. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Defining a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public Map&amp;lt;KeyType, DataType&amp;gt; HashMapName = new HashMap&amp;lt;&amp;gt;(); //Example syntax&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Example Declaration&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; pluginEnabled = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; isGodMode = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So inside on command you&#039;ll need this, the function name can be different but for the sake of simplicity it&#039;s best if you keep it the same. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;Player player = (Player) sender;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
togglePluginState(player);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void togglePluginState(Player player) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Notice how we use the player name as the key here,&lt;br /&gt;
&lt;br /&gt;
    // not the player object&lt;br /&gt;
&lt;br /&gt;
    String playerName = player.getName();&lt;br /&gt;
&lt;br /&gt;
    if (pluginEnabled.containsKey(playerName)) {&lt;br /&gt;
&lt;br /&gt;
        if (pluginEnabled.get(playerName)) {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, false);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin disabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, true);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    } else {&lt;br /&gt;
&lt;br /&gt;
        pluginEnabled.put(playerName, true); //If you want plugin disabled by default change this value to false.&lt;br /&gt;
&lt;br /&gt;
        player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== More Ideas for HashMaps  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A HashMap (or really any kind of Map in Java) is an association. It allows quick and efficient lookup of some sort of &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;value&#039;&#039;&#039;, given a unique &#039;&#039;&#039;key&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;. Anywhere this happens in your code, a Map may be your solution. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here are a few other ideas which are ideally suited to using Maps. As you will see, it doesn&#039;t have to be data that you store per player, but can be any kind of data that needs to be &amp;quot;translated&amp;quot; from one form to another. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Data Value Lookups  =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Integer&amp;gt; wool_colors = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;orange&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;magenta&amp;quot;, 2);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;light blue&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
   ..&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;black&amp;quot;, 15);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this in response to user commands - turn &amp;quot;green&amp;quot; into 13&lt;br /&gt;
&lt;br /&gt;
int datavalue = 0;&lt;br /&gt;
&lt;br /&gt;
if (wool_colors.containsKey(argument)) {&lt;br /&gt;
&lt;br /&gt;
    datavalue = wool_colors.get(argument);&lt;br /&gt;
&lt;br /&gt;
} else {&lt;br /&gt;
&lt;br /&gt;
    try { datavalue = Integer.parseInt(argument); }&lt;br /&gt;
&lt;br /&gt;
    catch (Exception e) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Saving/Loading a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you don&#039;t want an administrator to edit the data manually&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you need to save data in binary format (too complex to organize for YAML)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you want to avoid parsing block names and/or other objects from freeform text&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is very simple way how to save any HashMap. You can replace HashMap&amp;amp;amp;lt;String, Integer&amp;amp;amp;gt; with any type of HashMap you want. Let&#039;s take an example HashMap with code to save it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        HashMap&amp;lt;String, Integer&amp;gt; mapToSave = new HashMap&amp;lt;String,Integer&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
        public void save(HashMap&amp;lt;String, Integer&amp;gt; map, String path) {&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(map);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
//Handle I/O exceptions&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
save(mapToSave, getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see it&#039;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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public HashMap&amp;lt;String, Integer&amp;gt; load(String path) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
Object result = ois.readObject();&lt;br /&gt;
&lt;br /&gt;
//you can feel free to cast result to HashMap&amp;lt;String, Integer&amp;gt; if you know there&#039;s that HashMap in the file&lt;br /&gt;
&lt;br /&gt;
return (HashMap&amp;lt;String, Integer&amp;gt;)result;&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HashMap&amp;lt;Integer, String&amp;gt; loadedMap;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
String path = getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
File file = new File(path);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if (file.exists()) { // check if file exists before loading to avoid errors!&lt;br /&gt;
&lt;br /&gt;
loadedMap  = load(path);&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
You can use this &amp;quot;API&amp;quot; for saving/loading HashMaps, ArrayLists, and all Objects which implement Serializable or Externalizable interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;/** SLAPI = Saving/Loading API&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 * API for Saving and Loading Objects.&lt;br /&gt;
&lt;br /&gt;
 * Everyone has permission to include this code in their plugins as they wish :)&lt;br /&gt;
&lt;br /&gt;
 * @author Tomsik68&amp;lt;tomsik68@gmail.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
public class SLAPI&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; void save(T obj,String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(obj);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; T load(String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
T result = (T)ois.readObject();&lt;br /&gt;
&lt;br /&gt;
ois.close();&lt;br /&gt;
&lt;br /&gt;
return result;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Example implementation of this API: &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;I&#039;m skipping some part of code in this source&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public class Example extends JavaPlugin {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
private ArrayList&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onEnable() {&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
list = SLAPI.load(&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                //handle the exception&lt;br /&gt;
&lt;br /&gt;
                e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onDisable()	{&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
SLAPI.save(list,&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                 e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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&#039;re writing your own data object classes, and you may want to save their state using this technique, you should read about Java&#039;s Serializable or Externalizable interface. The only difference between Externalizable and Serializable is, that Serializable automatically takes all of class&#039;s fields and tries to serialize them, while Externalizable allows you to define method for reading and writing the Object. It&#039;s easy to add to your code, and it will make your data persistent with very little work required. No more parsing!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note #2: This API doesn&#039;t support changes. Once you change something in the class, data files saved with older version of your plugin won&#039;t load correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Tips &amp;amp; Examples =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.) Simplify your save structure&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.) Save version number along with data&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always remember, that you don&#039;t know what you&#039;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&#039;t know!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.) Migrate older files&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your plugin finds older version of some file, it should update the file accordingly and change version number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 元数据  ==&lt;br /&gt;
&lt;br /&gt;
Bukkit正在尝试尽可能的让制作插件变得更简单，所以带有一个键值例如Player, Entity, World，甚至是一个block的HashMaps都可以用元数据取缔掉。元数据是一些替代HashMap的方式。他可以允许你去自定义&amp;quot;fields&amp;quot;给Players, Entities, Worlds and Blocks.这些东西都是元数据类的一个成员(详见[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&#039;re creating an economy plugin, you would need a HashMap of Player and Float or Double. 如果用了元数据，你不必去做! You just attach to player new metadata 值, and that&#039;s it!&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== 为何要去使用元数据  ===&lt;br /&gt;
&lt;br /&gt;
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps. &lt;br /&gt;
* 元数据可以用来跟其他插件分享信息与数据。&lt;br /&gt;
&lt;br /&gt;
=== 为什么不去使用元数据&amp;lt;br&amp;gt;  ===&lt;br /&gt;
&lt;br /&gt;
* Slightly more difficult to get the value.&lt;br /&gt;
* It is not saved on shutdown (but then again, neither are any Maps that you create).&lt;br /&gt;
&lt;br /&gt;
=== 获取 &amp;amp;amp; 设置元数据  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {&lt;br /&gt;
  object.setMetadata(key, new FixedMetadataValue(plugin,value));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Object getMetadata(Metadatable object, String key, Plugin plugin) {&lt;br /&gt;
  List&amp;lt;MetadataValue&amp;gt; values = object.getMetadata(key);  &lt;br /&gt;
  for (MetadataValue value : values) {&lt;br /&gt;
     // Plugins are singleton objects, so using == is safe here&lt;br /&gt;
     if (value.getOwningPlugin() == plugin) {&lt;br /&gt;
        return value.value();&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
  return null;&lt;br /&gt;
}&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note: If you&#039;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.&lt;br /&gt;
&lt;br /&gt;
== Databases  ==&lt;br /&gt;
&lt;br /&gt;
Sometimes flat files aren&#039;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).&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there&#039;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!) &lt;br /&gt;
&lt;br /&gt;
=== SQLite  ===&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;s very straightforward and shouldn&#039;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]. &lt;br /&gt;
&lt;br /&gt;
SQLite is great for very simple databases, because there&#039;s no server concerns to set up. Just make a few calls to create a new database and table. It&#039;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&#039;s often easiest and fastest to get the SQL basics squared away with SQLite, even if you &amp;quot;graduate&amp;quot; to a more server-class database engine later.&lt;br /&gt;
&lt;br /&gt;
=== MySQL  ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 部署你的插件  ==&lt;br /&gt;
&lt;br /&gt;
 总之你写好的你的插件,如何把它变成一个可安装在服务端的jar文件呢?&lt;br /&gt;
 &lt;br /&gt;
 首先我们需要建立一个水桶服务器。 [http://wiki.bukkit.org/Setting_up_a_server 在这里] 能找到开服相关信息.接下来你需要把插件导出为一个.jar文件，&lt;br /&gt;
 &lt;br /&gt;
 这样你就能在你的服务器里运行它。 在Eclipse里, 右键工程，点击&#039;&#039;Run as &amp;gt; Maven install&#039;&#039;即可:&lt;br /&gt;
&lt;br /&gt;
[[Image:Maveninstall.png]]&lt;br /&gt;
&lt;br /&gt;
 将来呢, 当你对插件进行了改动后,在做上述操作之前，如果你想删除之前的.jar文件，你可以右键工程，点击 &#039;&#039;Run as &amp;gt; Maven clean&#039;&#039; 。 &lt;br /&gt;
 &lt;br /&gt;
 如果你在编译插件时遇到了问题,  请检查你的JDK是否正确安装， 并浏览[[建立工作区]]条目。&lt;br /&gt;
 &lt;br /&gt;
 如果你在Eclipse控制台看到了和JDK有关的错误，你可能需要手动调整JDK,因为Eclipse的检测可能发生了错误。 &lt;br /&gt;
 &lt;br /&gt;
 点击&#039;&#039;Window -&amp;gt; Preferences&#039;&#039;, 然后是&#039;&#039;Java -&amp;gt; Installed JREs&#039;&#039;。选中你最近安装的JDK作为Java运行环境（JRE）并添加, 并取消掉原来那个报错的JDK:&lt;br /&gt;
&lt;br /&gt;
[[Image:Jrelocation.png]]&lt;br /&gt;
&lt;br /&gt;
 如果你的工程成功被编译,JAR文件会生产在 &#039;&#039;target&#039;&#039; 目录下，具体在Eclipse的工作空间里名字为工程名的文件夹中。&lt;br /&gt;
 &lt;br /&gt;
 这个JAR文件就是一个能正常工作的Bukkit插件了。当然前提是你的plugin.yml里没出差错。&lt;br /&gt;
 &lt;br /&gt;
 你可以把这个jar文件丢到服务器的plugins文件夹里,重载或重启服务器,就可以测试你的新插件啦。&lt;br /&gt;
 &lt;br /&gt;
 为了连接上使用你自己的电脑开的服务器 ,在多人游戏的ip里填上你的本地ip即可。&lt;br /&gt;
 &lt;br /&gt;
 如果出现了错误而你又不能解决的话, 试试这里[http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], &lt;br /&gt;
 &lt;br /&gt;
 在这里提问[http://wiki.bukkit.org/IRC bukkitdev IRC channel],或者重新阅读wiki。&lt;br /&gt;
 &lt;br /&gt;
 当你做完了一个实用的插件,可以考虑把它发表在这里[http://dev.bukkit.org/ dev.bukkit]（为了Bukkit社区的访问量）。&lt;br /&gt;
 &lt;br /&gt;
 以上可知,JAR格式是默认压缩生成的(JAR文件基于ZIP文件的格式)。这么说来，在上传插件到Bukkit上时把JAR文件压缩成ZIP只会增加文件体积罢了。&lt;br /&gt;
 &lt;br /&gt;
 而且呢, 放在JAR中的config文件可以在检测不到插件目录（插件自动生成的文件夹，名字与插件名相同，里面是该插件的配置）下的config文件时自动生成。&lt;br /&gt;
 &lt;br /&gt;
 所以我们一般不把jar文件转换成其他格式。&lt;br /&gt;
&lt;br /&gt;
== 导入其它插件 ==&lt;br /&gt;
你可能会希望编辑其它那些有源代码的插件. 如果插件在它的目录下有&#039;&#039;pom.xml&#039;&#039;(大多数知名插件,例如WorldEdit和Essentials都有),你可以通过点击&#039;&#039;File -&amp;gt; Import&#039;&#039;来把它作为一个工程导入, 然后打开&#039;&#039;Maven&#039;&#039;项并选择&#039;&#039;Existing Maven Projects&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
[[Image:Importmaven.png]]&lt;br /&gt;
&lt;br /&gt;
然后选择&#039;&#039;pom.xml&#039;&#039;所在的目录,这个工程就应该出现在你的侧边栏里了。就像平常一样编辑并编译它吧。&lt;br /&gt;
&lt;br /&gt;
== 小贴士  ==&lt;br /&gt;
&lt;br /&gt;
=== 使玩家着火 ===&lt;br /&gt;
&lt;br /&gt;
 Bukkit API有很多碉堡的功能。以下是实现一些功能的代码片段。 &lt;br /&gt;
&lt;br /&gt;
下面的代码运行一个玩家使另一个玩家着火。通过类似于&#039;&#039;&#039;/ignite Notch&#039;&#039;&#039;这样的指令，这个指令会让 Notch 着火。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     // 使用 equalsIgnoreCase() 来忽视大小写&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;ignite&amp;quot;)) {&lt;br /&gt;
         // 确保玩家只输入了一个自变量(要被点着的玩家名).&lt;br /&gt;
         if (args.length != 1) {&lt;br /&gt;
             // 当return false时，该指令的使用信息会显示出来。&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 确保命令发出者是个玩家.&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can set other players on fire.&amp;quot;);&lt;br /&gt;
             sender.sendMessage(&amp;quot;This is an arbitrary requirement for demonstration purposes only.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 获取那个要被点着的玩家. 请注意下标起始是0，不是1.&lt;br /&gt;
         Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
 &lt;br /&gt;
         // 确保这个玩家在线。&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 让这个玩家着火1000 ticks (一秒钟大概有20ticks,所以总共是50秒).&lt;br /&gt;
         target.setFireTicks(1000);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 干掉玩家  ===&lt;br /&gt;
&lt;br /&gt;
 和标题一样.这里是要干掉某个玩家。 &lt;br /&gt;
&lt;br /&gt;
在你的onCommand方法里使用这个: &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;KillPlayer&amp;quot;)) {&lt;br /&gt;
         Player target = sender.getServer().getPlayer(args[0]);&lt;br /&gt;
          // 确保玩家在线.&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         target.setHealth(0); &lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这里有个小扩展,会使用一个爆炸来杀死玩家: &lt;br /&gt;
&lt;br /&gt;
 float explosionPower = 4F; //爆炸等级 - TNT的爆炸等级默认是4F&lt;br /&gt;
 Player target = sender.getWorld().getPlayer(args[0]);&lt;br /&gt;
 target.getWorld().createExplosion(target.getLocation(), explosionPower);&lt;br /&gt;
 target.setHealth(0);&lt;br /&gt;
&lt;br /&gt;
=== 创造一个假爆炸  ===&lt;br /&gt;
&lt;br /&gt;
这段代码会生产一个看起来和听起来与TNT/苦力怕产生的爆炸一样的效果。但是不会对实体和方块产生破坏。 在保留爆炸的艺术的同时又削弱了爆炸的威力，真是太有用了（&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onExplosionPrime(ExplosionPrimeEvent event) {	&lt;br /&gt;
     Entity entity = event.getEntity();&lt;br /&gt;
 &lt;br /&gt;
     // 如果事件和点燃的TNT有关,执行一些代码（又略）&lt;br /&gt;
     if (entity instanceof TNTPrimed) {&lt;br /&gt;
         entity.getWorld().createExplosion(entity.getLocation(), 0);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 使一个玩家对另外一个玩家隐形  ===&lt;br /&gt;
&lt;br /&gt;
 通过输入指令，能让指令执行者对特定的玩家隐形。 然而其他人仍然能看到指令执行者。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;HideMe&amp;quot;) &amp;amp;&amp;amp; args.length == 1) {&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can use this command!&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         // 检测指令执行者是玩家后，我们便可以安全的进行强制转换类操作。&lt;br /&gt;
        Player s = (Player) sender;&lt;br /&gt;
 &lt;br /&gt;
        // 获取命令执行者指定的玩家&lt;br /&gt;
        Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
        if (target == null) {&lt;br /&gt;
            sender.sendMessage(&amp;quot;Player &amp;quot; + args[0] + &amp;quot; is not online.&amp;quot;);&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        // 将 s 从 target 的视野中隐藏起来.&lt;br /&gt;
        target.hidePlayer(s);&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 在玩家准星所指处生产闪电 ===&lt;br /&gt;
&lt;br /&gt;
如下代码运行手持鱼竿的玩家通过点击来生成闪电 (当然要用准星瞄准)。 真是个简单好玩的恶作剧（。&lt;br /&gt;
&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onPlayerInteractBlock(PlayerInteractEvent event) {&lt;br /&gt;
     Player player = event.getPlayer();&lt;br /&gt;
     if (player.getItemInHand().getType() == Material.FISHING_ROD) {&lt;br /&gt;
         // 在给定坐标中生成一道闪电. 在本例中, 这个坐标是玩家准星瞄准的地方.&lt;br /&gt;
         // 只能指向200格以内的坐标.&lt;br /&gt;
         player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 自动格式 ===&lt;br /&gt;
&lt;br /&gt;
 Eclipse提供了将你的代码自动转成Oracle规定的格式, 修正了不符合常规的缩进, 空格等等。 在侧边栏选中你的项目,然后选择&#039;&#039;Source -&amp;gt; Format&#039;&#039;即可对其进行更改。&lt;br /&gt;
&lt;br /&gt;
== 插件需求专区  ==&lt;br /&gt;
&lt;br /&gt;
http://forums.bukkit.org/forums/plugin-requests.13/&lt;br /&gt;
&lt;br /&gt;
== 范例文件和样板  ==&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/wpeTPx7N Example.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have any more questions on this matter, don&#039;t hesitate to visit the BukkitDev [[IRC]] channel and ask!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:教程]]&lt;br /&gt;
[[Category:大篇幅条目]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1342</id>
		<title>Bukkit/插件开发教程</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1342"/>
		<updated>2015-08-31T02:42:29Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* (Player) Inventory Manipulation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
{{模板:待完善}}&lt;br /&gt;
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]&lt;br /&gt;
== 介绍  ==&lt;br /&gt;
&lt;br /&gt;
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.&lt;br /&gt;
&lt;br /&gt;
== 学习Java  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;理解这些教程需要具备Java这门编程语言的基本知识.&#039;&#039;&#039;如果你刚开始学习Java或者需要重温一下相关知识，下面有一个并不完整的网站列表可供参考.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;[http://docs.oracle.com/javase/tutorial/ Oracle&#039;s Java Tutorials]&amp;lt;/big&amp;gt; - 官方教程&lt;br /&gt;
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程&lt;br /&gt;
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程&lt;br /&gt;
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书&lt;br /&gt;
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.&lt;br /&gt;
&lt;br /&gt;
== 开发环境  ==&lt;br /&gt;
&lt;br /&gt;
在编写一个插件前 (或学习Java前) 你需要配置一个开发者环境，这个包括但不限于安装IDE (Integrated Development Environment，集成开发环境)。接下来的教程中有关于Eclipse上IDE的操作指南。&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;更多信息，请参见 [[Bukkit/插件开发教程-建立工作区|建立工作区]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
你&#039;&#039;&#039;必须&#039;&#039;&#039;下载&#039;&#039;&#039;Java&#039;&#039;&#039;开发者使用的Eclipse构建版本, &#039;&#039;&#039;不是&#039;&#039;&#039; &#039;&#039;&#039;Java EE&#039;&#039;&#039;开发者的版本。 Java EE 开发者的版本不包含此教程需要的Maven支持。&lt;br /&gt;
&lt;br /&gt;
== 新建插件开发项目  ==&lt;br /&gt;
=== 创建一个新的项目 ===&lt;br /&gt;
&lt;br /&gt;
在开始工作之前，你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击&#039;&#039;File -&amp;gt; New -&amp;gt; Project:&#039;&#039;来创建一个新的项目.&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject.png]] &lt;br /&gt;
&lt;br /&gt;
现在,打开&#039;&#039;Maven&#039;&#039;文件夹, 然后选择&#039;&#039;Maven Project&#039;&#039;.点击next,之后在下一个菜单中选择&#039;&#039;Create a simple project&#039;&#039;, 再次点击&#039;&#039;Next&#039;&#039;:&lt;br /&gt;
如果你看不到&#039;&#039;Maven&#039;&#039;文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject2.png]] &lt;br /&gt;
&lt;br /&gt;
现在，你需要给你的组用户命名，就像下面这样:&lt;br /&gt;
* 如果你拥有一个域名，package则填写逆转的域名地址. &lt;br /&gt;
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]&lt;br /&gt;
** 避免使用一个不属于你自己的域名.&lt;br /&gt;
* 没有域名? 这里有几种方法可供选择。&lt;br /&gt;
*# 在资源管理站点创建一个用户，比如GitHub或是sourceforge&lt;br /&gt;
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 使用你的邮箱.  例如: &amp;lt;username&amp;gt;@gmail.com格式的邮箱应输入com.gmail.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 最不济的方法.：  使用独特的组名命名方式，这应当是你最后的解决方法。&lt;br /&gt;
&lt;br /&gt;
以下几个地址不能作为package中的地址前缀:&lt;br /&gt;
* org.bukkit&lt;br /&gt;
* net.bukkit&lt;br /&gt;
* com.bukkit&lt;br /&gt;
* net.minecraft&lt;br /&gt;
&lt;br /&gt;
完成基础的组名以后，你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 &#039;&#039;TestPlugin&#039;&#039;的插件，那么完整的组名是&#039;&#039;io.github.&amp;lt;username&amp;gt;&#039;&#039;, 你的工程名也是 &#039;&#039;TestPlugin&#039;&#039;. 至于版本，默认即可，稍后可以修改。&lt;br /&gt;
完成向导:&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject3.png]] &lt;br /&gt;
&lt;br /&gt;
如果这是你首次使用Eclipse, 点击右上角的&amp;quot;X&amp;quot; 来关闭Welcome提示页面. 现在，你的窗口视图看起来就像下面的图片这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Eclipsemain.png]] &lt;br /&gt;
&lt;br /&gt;
点击工程名称右边的箭头来进行下一步，现在我们正式开始。&lt;br /&gt;
=== 添加Bukkit API ===&lt;br /&gt;
&lt;br /&gt;
在开发插件之前，你需要添加 Bukkit API库文件到你的项目，作为一个dependency, 你也可以添加其他你想实用的API.&lt;br /&gt;
&lt;br /&gt;
找到项目文件夹中部的&#039;&#039;pom.xml&#039;&#039;并双击进行编辑. 点击&#039;&#039;pom.xml&#039;&#039;中部的 tab,你将会看到下图所示内容:&lt;br /&gt;
&lt;br /&gt;
[[Image:Pomeditor.png]]&lt;br /&gt;
&lt;br /&gt;
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;build&amp;gt;&lt;br /&gt;
       &amp;lt;plugins&amp;gt;&lt;br /&gt;
           &amp;lt;plugin&amp;gt;&lt;br /&gt;
               &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;
               &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
               &amp;lt;configuration&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;amp;lt;&amp;lt;/nowiki&amp;gt;source&amp;gt;1.7&amp;lt;/source&amp;gt;&lt;br /&gt;
                   &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;&lt;br /&gt;
               &amp;lt;/configuration&amp;gt;&lt;br /&gt;
           &amp;lt;/plugin&amp;gt;&lt;br /&gt;
       &amp;lt;/plugins&amp;gt;&lt;br /&gt;
    &amp;lt;/build&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你或许想使用其他版本,例如 &amp;lt;code&amp;gt;1.8&amp;lt;/code&amp;gt; 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码&amp;quot;error&amp;quot;中建议你更改语言版本.同意即可.&lt;br /&gt;
&lt;br /&gt;
在位于代码中段的&#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit&#039;s repository的地址): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;repositories&amp;gt;&lt;br /&gt;
        &amp;lt;repository&amp;gt;&lt;br /&gt;
            &amp;lt;id&amp;gt;bukkit-repo&amp;lt;/id&amp;gt;&lt;br /&gt;
            &amp;lt;url&amp;gt;http://repo.bukkit.org/content/groups/public/&amp;lt;/url&amp;gt;&lt;br /&gt;
        &amp;lt;/repository&amp;gt;&lt;br /&gt;
    &amp;lt;/repositories&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后继续在 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;dependencies&amp;gt;&lt;br /&gt;
        &amp;lt;dependency&amp;gt;&lt;br /&gt;
            &amp;lt;groupId&amp;gt;org.bukkit&amp;lt;/groupId&amp;gt;&lt;br /&gt;
            &amp;lt;artifactId&amp;gt;bukkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
            &amp;lt;version&amp;gt;1.7.2-R0.3&amp;lt;/version&amp;gt;&lt;br /&gt;
            &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;
            &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;
        &amp;lt;/dependency&amp;gt;&lt;br /&gt;
    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.&lt;br /&gt;
&lt;br /&gt;
当你完成上述步骤后,&#039;&#039;pom.xml&#039;&#039;中的内容应该是这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Finishedpom.png]]&lt;br /&gt;
&lt;br /&gt;
通过菜单 &#039;&#039;File -&amp;gt; Save&#039;&#039;或者按住 &amp;lt;code&amp;gt;Ctrl + S&amp;lt;/code&amp;gt; 来保存&#039;&#039;pom.xml&#039;&#039;文件 .  之后, 右键项目标题，然后依次选择 &#039;&#039;Maven -&amp;gt; Update Project&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Bukkit Java文档 ===&lt;br /&gt;
&lt;br /&gt;
 如果你曾经使用过Eclipse和Java，你会知道当你将鼠标准心移至class或者 method中的代码部分时，一个含有相关文档内容的黄色小框会弹出来. 这就是（Java的）Java文档。&lt;br /&gt;
 &lt;br /&gt;
 你可以[http://download.oracle.com/javase/6/docs/api/ Oracle website 在线获取]它.。Bukkit同样拥有文档，这些文档内容包含了关于API中method和class的有用描述。&lt;br /&gt;
 &lt;br /&gt;
 你可以在[http://jd.bukkit.org/ 这]找到它 (Beta版的Java文档在[http://jd.bukkit.org/ 这]找, 新开发出来的Java文档在  &lt;br /&gt;
 &lt;br /&gt;
 [http://jd.bukkit.org/ 这]) 。为了在Eclipse里使用Java文档,这样的话当你的鼠标停留在Bukkit的class和method上时，&lt;br /&gt;
 &lt;br /&gt;
 （Bukkit的）Java文档就会冒出来。  &lt;br /&gt;
JavaDoc in spigot [https://hub.spigotmc.org/javadocs/bukkit/ Click Here]&lt;br /&gt;
&lt;br /&gt;
 首先右键在侧边栏里的&amp;quot;Maven Dependencies&amp;quot;下面的Bukkit jar,选择&amp;quot;Properties&amp;quot;.选中窗口左边的&#039;&#039;Javadoc Location&#039;&#039;, 并粘贴如下网址 &lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;http://jd.bukkit.org/&#039;&#039;&#039; (或者上面的测试版/最新版 Java文档链接也好) 到&amp;quot;Javadoc URL&amp;quot;下面的输入框内. 就像这样: &lt;br /&gt;
&lt;br /&gt;
[[Image:Bukkitjavadocs.png]] &lt;br /&gt;
&lt;br /&gt;
 点击 &amp;quot;Validate&amp;quot;,然后再点击&amp;quot;OK&amp;quot;就完成了.现在Bukkit Javadocs已经连接到Bukkit提供的资源,同时你也可以通过Eclipse来获得帮助类文档信息了.&lt;br /&gt;
&lt;br /&gt;
=== 创建一个包 ===&lt;br /&gt;
&lt;br /&gt;
 现在你需要创建一个包，它将储存所有我们需要使用的Java类文件. 右键展开&#039;&#039;src/main/java&#039;&#039;折叠并选择 &#039;&#039;New &amp;amp;gt; Package&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
[[Image:Newpackage.png]] &lt;br /&gt;
&lt;br /&gt;
 包名的话，先写上你的组名，然后加个点，再写上你的小写Artifact ID。举例, 如果你的组名是&#039;&#039;io.github.name&#039;&#039;，你的artifact名是 &#039;&#039;TestPlugin&#039;&#039;。&lt;br /&gt;
 &lt;br /&gt;
 你的包名就应该是&#039;&#039;io.github.name.testplugin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== 创建插件的类文件 ===&lt;br /&gt;
&lt;br /&gt;
既然我们已经建立好了我们的项目，我们接下来可以开始添加类文件以及制作插件了. 插件的主类指的是拓展(extends)JavaPlugin的类文件. 在你的插件中只能有一个类文件拓展JavaPlugin，无论直接还是间接. 先创建你的主类文件并保持名称与插件名一致是个很好的习惯.右键你之前新建的包,并选择&amp;amp;nbsp; &#039;&#039;New &amp;amp;gt; Class&#039;&#039;.你将会创建一个新的类文件，就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$GroupName}.{$ArtifactName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$ArtifactName} extends JavaPlugin {&lt;br /&gt;
    &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
警告： 插件绝对不应该调用自己的构造函数并实例化&lt;br /&gt;
&lt;br /&gt;
=== 创建plugin.yml ===&lt;br /&gt;
&lt;br /&gt;
你已经创建了项目和主类，如果你想要Bukkit能够加载你的插件，你还必须创建 &#039;&#039;&#039;[[Plugin YAML|plugin.yml]]&#039;&#039;&#039; 文件. 这个文件包含有基础的插件信息，如果缺失这个文件，你的插件也将会失效. 此时你需要右键&#039;&#039;src/main/resources&#039;&#039;. 选择&#039;&#039;New &amp;amp;gt; File&#039;&#039;. 命名为&amp;quot;&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;&amp;quot; 并右键完成新文件的创建.Eclipse会打开默认的文本编辑窗口来显示&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;文件中空白的内容并提供编辑. &#039;&#039;(Hint:&amp;amp;nbsp;如果你想要使得你的工作空间更加规整，关闭文本编辑器并将&#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件拖到主工作空间(拖到文件目录的右边) 之后你就可以在eclipse中编辑文件了.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
有三个基础的内容需要在plugin.yml写明. &lt;br /&gt;
: name:  插件名称. &lt;br /&gt;
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 插件主类的完整，合法名称] . &lt;br /&gt;
: version: 插件当前版本号. &lt;br /&gt;
&lt;br /&gt;
最简单的 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件内就像这样&amp;amp;nbsp;: &lt;br /&gt;
&lt;br /&gt;
 name: {插件名称}&lt;br /&gt;
 main: {包名}.{主类}&lt;br /&gt;
 version: {版本号}&lt;br /&gt;
&lt;br /&gt;
 PS:插件的包名经常会包括插件的名称，所以看到这个的时候不要感到惊讶。&lt;br /&gt;
    你的主类是否是你的插件名取决于你之前的命名方式，时刻记住这一点很重要。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;更多例子&#039;&#039;&#039;, 请看[[#Example_Files_and_Templates]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
此时你的插件已经可以被Bukkit加载了,同时服务端日志也会开始记录你的插件. 不过它现在什么用都没有!&lt;br /&gt;
&lt;br /&gt;
== onEnable()和onDisable()方法  ==&lt;br /&gt;
&lt;br /&gt;
这些方法将在插件启用与卸载时生效. 默认情况下，你的插件在被加载时会调用这些方法，所以你可以在这里注册你需要用到的事件和提供一些调试信息. &amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 方法会在插件启用时被调用, 需要包含初始化插件的逻辑语句. &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法会在插件卸载时被调用，需要包含清理(clean up)插件的逻辑语句和连接声明(associated state)。此外，插件可以重写 &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; 方法来在插件载入时执行附加的逻辑语句。&lt;br /&gt;
&lt;br /&gt;
=== onEnable()和onDisable()方法介绍  ===&lt;br /&gt;
&lt;br /&gt;
在前面的章节中，我们在主类创建了&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法. 此时，这些代码看起来就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
     @Override&lt;br /&gt;
     public void onEnable() {&lt;br /&gt;
         // 插件载入时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     @Override&lt;br /&gt;
     public void onDisable() {&lt;br /&gt;
         // 插件卸载时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这些方法已经创建，但目前它们还没有任何作用.&lt;br /&gt;
注意: 不需要添加代码来实现输出信息&amp;quot;{$PluginName} has been enabled!&amp;quot; ，因为bukkit会自动输出此类信息&lt;br /&gt;
有关更多事件的信息请查看 [[Event_API_Reference|这里]].&lt;br /&gt;
&lt;br /&gt;
=== 发送提示信息 ===&lt;br /&gt;
&lt;br /&gt;
 插件能够输出信息至控制台与服务器日志。这是通过调用插件记录器里正确的方法来实现的。首先我们必须调用 &amp;lt;code&amp;gt;getLogger()&amp;lt;/code&amp;gt; 方法来检索该插件的记录器，&lt;br /&gt;
 &lt;br /&gt;
 然后我们就可以开始记录信息了。&lt;br /&gt;
&lt;br /&gt;
我们通过调用&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法可以实现输出信息。 我们需要在&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法下面插入如下代码：&lt;br /&gt;
&lt;br /&gt;
 getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
在 &amp;lt;code&amp;gt;&#039;&#039;&#039;onDisable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法里面，你也可以做类似的事情，不过要改改输出信息。&lt;br /&gt;
&lt;br /&gt;
此时你的主类文件就像这样: &lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
         @Override&lt;br /&gt;
         public void onEnable() {&lt;br /&gt;
             getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public void onDisable() {&lt;br /&gt;
         getLogger().info(&amp;quot;onDisable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 防止重载后插件失效 ===&lt;br /&gt;
&lt;br /&gt;
你需要知道的是，插件并不只会伴随服务器关闭、启动而重载，你的插件也可能会被其他插件启用、关闭，或是在服务器运行时使用/reload命令. &lt;br /&gt;
假设服务器启动之后一会儿后，你的插件才被启用，这时便是危险的,因为服务器中可能已经有玩家在线、额外的世界被加载以及很多预想不到的不同之处(与&amp;quot;插件同服务器同时加载&amp;quot;的情况相比较). &lt;br /&gt;
&lt;br /&gt;
举个例子来说:&lt;br /&gt;
* 你有一个用来储存HashMap中的玩家登入信息的插件&lt;br /&gt;
* 你希望该信息对所有玩家都可见&lt;br /&gt;
* 一名管理员使用/reload命令&lt;br /&gt;
* 你的插件被卸载，所有储存的数据都丢失了&lt;br /&gt;
* 你的插件被再次启用时，一些玩家已经在线&lt;br /&gt;
* 这些玩家在HashMap并没有储存的信息&lt;br /&gt;
* 你试图重新获得他们的信息，但是失败了!&lt;br /&gt;
&lt;br /&gt;
为了插件重载以后能够正常工作, 你需要在插件启用时加载所有已经在线的玩家的信息并将之储存在HashMap中.&lt;br /&gt;
&lt;br /&gt;
 for (Player player : Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
     playerList.put(player.getName(), playerData(player));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== 监听器  ==&lt;br /&gt;
监听器是一种方法被调用来对事件作出反应的类.所有的监听器使用 org.bukkit.event.Listener接口.更多有关监听器创建的细节,&lt;br /&gt;
:: &#039;&#039; 请查看: [[Event API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 指令  ==&lt;br /&gt;
&lt;br /&gt;
=== onCommand() 方法  ===&lt;br /&gt;
&lt;br /&gt;
 现在你已经知道如何注册一个事件并做出响应,但是如果你只是想要在命令输入之后做出响应呢? 你可以使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 这个代码的作用是当玩家输入“/”时，监听该操作并执行相关语句&amp;quot;/&amp;quot; . 举个例子来说.输入 &amp;quot;/do something&amp;quot; 将会执行 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 此种情况下，因为没有特定的行为被编程，所以并不会发生任何事情。&lt;br /&gt;
&lt;br /&gt;
 请避免使用和bukkit所提供的指令重名的指令, 然后深思你的指令名的唯一性。 &lt;br /&gt;
 &lt;br /&gt;
 例如.指令&amp;quot;give&amp;quot;已经被好几个插件使用了, 如果你执行了另外一个&amp;quot;give&amp;quot;指令, 你的插件将会和这些插件冲突。&lt;br /&gt;
&lt;br /&gt;
 你必须在插件的&amp;quot;plugins.yml&amp;quot;注册你的指令 否则这个方法将不会被触发。&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法必须返回一个布尔值（true或false）。 &lt;br /&gt;
 &lt;br /&gt;
 如果返回值是true，你不会看到什么明显的事情发生。 &lt;br /&gt;
 &lt;br /&gt;
 但如果返回值是false，则会返回你的plugin.yml里的&#039;usage:property&#039;然后发送给命令使用者. &lt;br /&gt;
&lt;br /&gt;
当使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时, 你需要填写4个参数. &lt;br /&gt;
&lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 发送命令的对象&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;Command cmd&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行的指令&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String commandLabel&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行指令的别名&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String[] args&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 该指令的自变量数组。&lt;br /&gt;
 &lt;br /&gt;
例如.指令 &#039;&#039;/hello abc def&#039;&#039; 中， &#039;&#039;abc&#039;&#039; 会被存放进args[0]中, &#039;&#039;def&#039;&#039; 被存放进args[1]中。&lt;br /&gt;
&lt;br /&gt;
==== 设置命令  ====&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { // 如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
     // 所需要执行的事（此处略）&lt;br /&gt;
         return true;&lt;br /&gt;
     } //如果以上内容成功执行，则返回true &lt;br /&gt;
     // 如果执行失败，则返回false.&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 每当使用&amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时， 在每个方法后面都加上一个 return false; 是一个惯例。 &lt;br /&gt;
 &lt;br /&gt;
 返回false会显示在plugin.yml里设置的usage信息 (看下面). 用这种方法的话，一旦执行出错便会显示usage信息。&lt;br /&gt;
&lt;br /&gt;
 每当方法return一个值的时候，这个方法就会退出，所以return true的时候，在它下面的代码就会被跳过, &lt;br /&gt;
 &lt;br /&gt;
 除非return语句在一个if的嵌套中，或者类似的嵌套情况。 &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;.equalsIgnoreCase(&amp;quot;basic&amp;quot;)&#039;&#039;&#039;&amp;lt;/code&amp;gt;代表忽略英文大小写. 在这种情况下,字符串&amp;quot;BAsIc&amp;quot; 和 &amp;quot;BasiC&amp;quot;和 basic相同，代码会被照常执行。&lt;br /&gt;
&lt;br /&gt;
和以前一样，加两行import在你的java文件头&lt;br /&gt;
&lt;br /&gt;
 import org.bukkit.command.Command;&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.command.CommandSender;&lt;br /&gt;
&lt;br /&gt;
=== 在Plugin.yml中添加你的指令  ===&lt;br /&gt;
&lt;br /&gt;
你需要添加你的指令到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件里. 如下是指令/basic添加到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039;的例子（请在plugins.yml的末尾添加如下代码）: &lt;br /&gt;
&lt;br /&gt;
commands:&lt;br /&gt;
   basic:&lt;br /&gt;
      description: This is a demo command.&lt;br /&gt;
      usage: /&amp;lt;command&amp;gt; [player]&lt;br /&gt;
      permission: &amp;lt;plugin name&amp;gt;.basic&lt;br /&gt;
      permission-message: You don&#039;t have &amp;lt;permission&amp;gt;&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;basic&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令名称&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;description&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令描述&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;usage&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 在oncommand方法return false后显示的用法提示。尽量简洁, 使别人能够理解指令是什么以及如何使用它。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被一些显示帮助信息的插件使用，用来决定向玩家显示哪个指令（完全不对劲吧！）。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission-message&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 当玩家使用了这个指令而没有权限时输出的信息。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 PS:yml文件使用两个空格作为制表符,使用tab键输入制表符会导致错误。&lt;br /&gt;
&lt;br /&gt;
===控制台指令vs玩家指令===&lt;br /&gt;
&lt;br /&gt;
 你可能注意到了上文的&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt;参数. &amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt; 是个Bukkit接口，&lt;br /&gt;
 &lt;br /&gt;
 它有两个（对插件编写者）有用的子类：&amp;lt;code&amp;gt;&#039;&#039;&#039;Player&#039;&#039;&#039;&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;&#039;&#039;&#039;ConsoleCommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 所以当你编写插件的时候, 确定这个指令从控制台发出后能完全正常工作是十分必要的,&lt;br /&gt;
 &lt;br /&gt;
 那些只能由在线玩家执行的指令 &#039;&#039;只能&#039;&#039; 由一个在线玩家执行。&lt;br /&gt;
 &lt;br /&gt;
 有的插件在判断命令执 行对象不是玩家时仅仅用return进行处理 (比如当有人从控制台发出指令), &lt;br /&gt;
 &lt;br /&gt;
 即便这些指令能够在控制台完美运行（比如改变天气的指令）。&lt;br /&gt;
&lt;br /&gt;
一个判断sender的方法: &lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { //如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
             // 所需要执行的事（此处略）&lt;br /&gt;
             return true;&lt;br /&gt;
         } else if (cmd.getName().equalsIgnoreCase(&amp;quot;basic2&amp;quot;)) {&lt;br /&gt;
             if (!(sender instanceof Player)) { //如果sender与Player类不匹配&lt;br /&gt;
                 sender.sendMessage(&amp;quot;This command can only be run by a player.&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 Player player = (Player) sender;&lt;br /&gt;
                    // 所需要执行的事（此处略）&lt;br /&gt;
             }&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 在这个例子里，指令&#039;&#039;&#039;basic&#039;&#039;&#039;能由任何人发出 - 一个在线的玩家, 或者服务器后台的op. &lt;br /&gt;
 &lt;br /&gt;
 但是指令&#039;&#039;&#039;basic2&#039;&#039;&#039;只能由一个在线的玩家发出. &lt;br /&gt;
&lt;br /&gt;
 大体上说,你应该允许尽可能多的指令在控制台和玩家聊天框里都正常执行. &lt;br /&gt;
 &lt;br /&gt;
 需要在线玩家执行的指令可以使用以上例子中的机制来检测&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;是一个玩家. &lt;br /&gt;
 &lt;br /&gt;
 很多指令广泛依靠于一些玩家的属性（例如可以被tp或被给予物品等等）。&lt;br /&gt;
&lt;br /&gt;
 如果你想更进一步, 你可以对你的指令的自变量做一些额外的检测， 例如传送指令在提供玩家ID的情况下（也只有在这种情况下）可以在控制台被执行。&lt;br /&gt;
&lt;br /&gt;
=== 使用独立的 CommandExecutor class  ===&lt;br /&gt;
&lt;br /&gt;
 以上的例子必须把 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法放在插件的主类里。&lt;br /&gt;
 &lt;br /&gt;
 对于小插件来说，这是极好的,但如果你要写一个大点的插件,把你的 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法放在独立的类里将会很有意义。&lt;br /&gt;
 &lt;br /&gt;
 幸运的是，这并不难: &lt;br /&gt;
&lt;br /&gt;
*在你的包里创建一个新类. 命名为&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;之类的 (当然，要把&#039;&#039;&#039;MyPlugin&#039;&#039;&#039;替换成你制作的插件的名字)。 这个类 &#039;&#039;&#039;必须&#039;&#039;&#039; 继承Bukkit 的&#039;&#039;&#039;CommandExecutor&#039;&#039;&#039;接口。&lt;br /&gt;
&lt;br /&gt;
*在你的插件的&amp;lt;code&amp;gt;&#039;&#039;&#039;onEnable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法中,你需要实例化你创建的命令执行类(CommandExecutor class), 然后做一个像下面一样的调用：&amp;lt;code&amp;gt;getCommand(&amp;quot;basic&amp;quot;).setExecutor(myExecutor);&amp;lt;/code&amp;gt;&lt;br /&gt;
 其中&amp;quot;basic&amp;quot;是我们需要操控的指令, &amp;lt;code&amp;gt;myExecutor&amp;lt;/code&amp;gt; 是我们创建的实例.&lt;br /&gt;
&lt;br /&gt;
废话少说直接吔我例子: &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyPlugin.java&#039;&#039;&#039; (插件的主类): &lt;br /&gt;
 @Override&lt;br /&gt;
 public void onEnable() {&lt;br /&gt;
         // 如果你没有在plugins.yml注册过指令的话，此处会抛出空指针异常！&lt;br /&gt;
         this.getCommand(&amp;quot;basic&amp;quot;).setExecutor(new MyPluginCommandExecutor(this));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;MyPluginCommandExecutor.java&#039;&#039;&#039;（独立CommandExecutor类）: &lt;br /&gt;
 public class MyPluginCommandExecutor implements CommandExecutor {&lt;br /&gt;
         private final MyPlugin plugin;&lt;br /&gt;
 &lt;br /&gt;
         public MyPluginCommandExecutor(MyPlugin plugin) {&lt;br /&gt;
             this.plugin = plugin; // Store the plugin in situations where you need it.&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
             // 和以前一样执行就好…&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 请注意我们是如何把主类里的对象指向&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;的. 这允许我们轻易的使用主类对象的方法（如果我们需要的话）。&lt;br /&gt;
&lt;br /&gt;
 这么做的话, 我们便能更好的组织代码 - 如果 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法又大又复杂, 它就能被划分为子类，从而不使插件的主类显得凌乱。&lt;br /&gt;
&lt;br /&gt;
 PS：如果你的插件含有多个指令的话, 你需要为每一个指令单独设立一个commandexecutor.&lt;br /&gt;
&lt;br /&gt;
=== 写一个安全的指令 ===&lt;br /&gt;
 当写一个指令的时候,别进行任何假设（很重要）, 例如假设执行者一定是个玩家. 请牢记以下原则:&lt;br /&gt;
&lt;br /&gt;
==== 在命令执行前确定发出者是玩家 ====&lt;br /&gt;
&lt;br /&gt;
使用简单的例子来进行检测:&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (sender instanceof Player) {&lt;br /&gt;
             Player player = (Player) sender;&lt;br /&gt;
             // do something&lt;br /&gt;
         } else {&lt;br /&gt;
             sender.sendMessage(&amp;quot;You must be a player!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
             // do something&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 检测自变量个数 ====&lt;br /&gt;
&lt;br /&gt;
 别老是认为玩家都能打对正确数量的自变量. &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (args.length &amp;gt; 4) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Too many arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         } &lt;br /&gt;
         if (args.length &amp;lt; 2) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Not enough arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 在获取玩家之前先检测他们是否在线 ====&lt;br /&gt;
&lt;br /&gt;
 有时你想通过命令发出者打出的ID来获取其他玩家，请确保他们在线再说！&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         Player target = (Bukkit.getServer().getPlayer(args[0]));&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not online!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 如果你想对离线玩家进行操作，&amp;lt;code&amp;gt;&#039;&#039;&#039;OfflinePlayer&#039;&#039;&#039;&amp;lt;/code&amp;gt;类提供了基础操作的方法.&lt;br /&gt;
&lt;br /&gt;
== 插件的配置/设置  ==&lt;br /&gt;
Bukkit API 为插件提供了一种简便的方法，用来管理用户自定义的设置。同时，通过这种方法也可以储存数据。&lt;br /&gt;
:: &#039;&#039;详情请查看: [[Configuration API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 权限  ==&lt;br /&gt;
&lt;br /&gt;
利用新的Bukkit API来设置权限是再容易不过的事情. 检测玩家是否拥有权限，可以使用以下代码: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;if (player.hasPermission(&amp;quot;some.pointless.permission&amp;quot;)) {&lt;br /&gt;
   //执行此段代码(有权限)&lt;br /&gt;
} else {&lt;br /&gt;
   //执行此段代码(无权限)&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你也可以检测一个权限节点是否被定义过，(等效于 Java 语言中的&#039;&#039;&#039;null&#039;&#039;&#039;)，使用下面的方法: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;boolean isPermissionSet(String name)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你或许对此抱有疑惑:为什么这段代码中没有任何用户群的信息？因为它们并不是必需的. 之前这段代码想要实现的主要功能之一就是根据用户群格式化聊天信息. 但其实使用权限就可以很容易地完成这项工作.你可以在聊天插件的配置文件中定义权限与前缀的对应关系. 举例来说，&amp;quot;someChat.prefix.admin&amp;quot; 权限可以对应前缀[Admin].当拥有此权限的玩家发出聊天信息时 ，聊天前缀便是[Admin]. &lt;br /&gt;
&lt;br /&gt;
另一个想要实现的主要功能则是给所有拥有权限的在线用户发一条信息.你可以这样做: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;for (Player player: Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
    if (player.hasPermission(&amp;quot;send.receive.message&amp;quot;)) {&lt;br /&gt;
        player.sendMessage(&amp;quot;You were sent a message&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
看到这里你可能会问，若是没有权限组我该如何配置玩家的 [[权限]] ？既然 [[Bukkit]] API 本身并没有提供用户组的预设, 你必须通过安装一款权限组插件，像 [[permissionsBukkit]]  这样的插件来帮助你配置用户群. &#039;&#039;&#039;API 只是提供接口而不是提供implementation.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
=== 配置权限 ===&lt;br /&gt;
{{Hide|标题=译者注 |内容= 其一，以下代码中部分内容将以&#039;&#039;//文字&#039;&#039;翻译，使用时请不要加入其中内容，仅做教程中的翻译参考；&lt;br /&gt;
其二，default:true在下面翻译为全员默认拥有该权限，default:false则翻译为全员默认没有该权限，请注意理解。 }}&lt;br /&gt;
如果你想要更多地控制你的权限，例如设置权限的默认值或是子权限节点，不如将权限节点加入你的 &#039;&#039;[[plugin.yml]]&#039;&#039;文件.这并不是必须的，但我们推荐你这样做. 下面是一个实例，显示了当你将权限添加在&#039;&#039;plugin.yml&#039;&#039;文件末尾时的样子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands //给予使用doorman所有命令的权限&lt;br /&gt;
        children:  //子权限节点&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&lt;br /&gt;
    doorman.kick:&lt;br /&gt;
        description: Allows you to kick a user  //允许你踢出一名用户&lt;br /&gt;
        default: op  //OP默认拥有该权限&lt;br /&gt;
    doorman.ban:&lt;br /&gt;
        description: Allows you to ban a user  //允许你封禁一名用户&lt;br /&gt;
        default: op //OP默认拥有该权限&lt;br /&gt;
    doorman.knock:&lt;br /&gt;
        description: Knocks on the door! //敲门&lt;br /&gt;
        default: true //全员默认拥有该权限&lt;br /&gt;
    doorman.denied:&lt;br /&gt;
        description: Prevents this user from entering the door  //禁止拥有该权限节点的用户进入门内&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
值得注意的是,你的插件使用的每个权限都被定义为 &#039;&#039;permissions&#039;&#039; 这个节点的子权限节点. 每个权限都能够拥有一个描述，默认值以及子权限节点。&lt;br /&gt;
==== 默认值  ====&lt;br /&gt;
&lt;br /&gt;
当一个权限没有明确是否为玩家所拥有&amp;amp;nbsp;，&#039;&#039;hasPermission&#039;&#039;&amp;lt;ref&amp;gt;这是一个检测玩家是否拥有指定权限节点的方法&amp;lt;/ref&amp;gt;&amp;amp;nbsp;将返回false&amp;lt;ref&amp;gt;该方法返回的boolean值&amp;lt;/ref&amp;gt;. 在你的[[plugin.yml]] you can change this by setting the default node to be one of four values: &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;true&#039;&#039;&#039; - 全员默认拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;false&#039;&#039;&#039; - 全员默认没有该权限. &lt;br /&gt;
*&#039;&#039;&#039;op&#039;&#039;&#039; - 如果玩家是一名OP&amp;lt;ref&amp;gt;Operator，服务器管理员&amp;lt;/ref&amp;gt;，那么他将拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;not op&#039;&#039;&#039; - 如果玩家不是一名OP，那么他将拥有该权限.&lt;br /&gt;
&lt;br /&gt;
==== 子权限  ====&lt;br /&gt;
&lt;br /&gt;
在这之前你可能已经习惯了通过权限节点自动分配所有的子权限&amp;lt;ref&amp;gt;原句:Before now you will probably be used to the * permission to automatically assign all sub permissions.此处的sub permissions应是同上文“你的插件使用的每个权限都被定义为 permissions 这个节点的子权限节点.”相照应&amp;lt;/ref&amp;gt;. 现在这种方法已经随着bukkit API&amp;lt;ref&amp;gt;应用程序编程接口(Application Programming Interface)&amp;lt;/ref&amp;gt;的更新而改变，现在你可以自己定义子权限了. 这将使得插件具有更多的灵活性. 下面是一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands  //翻译见上&lt;br /&gt;
        children:&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
在这里，&#039;&#039;doorman.*&#039;&#039; 权限拥有几个子权限并分配在其下. 子权限工作的方式就是当&#039;&#039;doorman.*&#039;&#039; 权限节点被设置为true时,子权限也会随之设置为 &#039;&#039;plugin.yml&#039;&#039;中的默认值. 然而当&#039;&#039;doorman.*&#039;&#039; 被设置为false时，所有子权限都会设置为与默认值相反的值（true-&amp;gt;false,false-&amp;gt;true）.&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设置你自己的权限  ===&lt;br /&gt;
&lt;br /&gt;
如果你希望想知道更多有关开发你自己的权限插件的事情(即是设置权限的管理类插件) ，那么请前往查看教程&amp;amp;nbsp;[[Developing a permissions plugin]].&lt;br /&gt;
&lt;br /&gt;
== Scheduling Tasks and Background Tasks  ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 方块操控&amp;lt;br&amp;gt;  ==&lt;br /&gt;
&lt;br /&gt;
这是一个很容易的方法创建、获取或修改一个方块, 例如当你想要修改往你头上数第五格的方块时, 首先你需要做的是获取你当前的坐标点, 然后往你当前的坐标点的Y轴加上五, 获取目标方块后我们便能够很容易地修改它了, 例子如下:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@EventHandler&lt;br /&gt;
public void onPlayerMove(PlayerMoveEvent event) {&lt;br /&gt;
    // 得到玩家当前的坐标.&lt;br /&gt;
    Location loc = event.getPlayer().getLocation();&lt;br /&gt;
    // 设置目标坐标为玩家头顶 5 格处.&lt;br /&gt;
    loc.setY(loc.getY() + 5);&lt;br /&gt;
    // 得到目标坐标的方块&lt;br /&gt;
    Block b = loc.getBlock();&lt;br /&gt;
    // 将目标坐标的方块更改为石头 (stone).&lt;br /&gt;
    b.setType(Material.STONE);&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
上面的代码获取了玩家的所在位置，获取头上的第五个方块，并且放上一个石头（stone）。 Note that once you have a &amp;lt;code&amp;gt;Block&amp;lt;/code&amp;gt;, there are other things you can do besides set its type. 你可以查阅JavaDoc获取更多的东西。&lt;br /&gt;
&lt;br /&gt;
你可以用这样相似的观念去生成一些建筑，并且还可以用编程的方式通过算法生成一些独特的方块（这里指由很多个方块拼在一起的四四方方的方块）。比如，去生成一个实心的方块建筑，你可能在生成建筑的时候还需要用到&amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;循环，去一遍遍循环来生成这些建筑，填满这些建筑等。&lt;br /&gt;
下面，是一个在指定坐标生成一个实心的方块建筑的实例方法：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void generateCube(Location loc, int length) {&lt;br /&gt;
    // 放置这个方块最中间的方块并且获取这个方块的坐标。&lt;br /&gt;
    // Uses getBlockN() instead of getN() to avoid casting to an int later.&lt;br /&gt;
    int x1 = loc.getBlockX(); &lt;br /&gt;
    int y1 = loc.getBlockY();&lt;br /&gt;
    int z1 = loc.getBlockZ();&lt;br /&gt;
&lt;br /&gt;
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.&lt;br /&gt;
    int x2 = x1 + length;&lt;br /&gt;
    int y2 = y1 + length;&lt;br /&gt;
    int z2 = z1 + length;&lt;br /&gt;
&lt;br /&gt;
    World world = loc.getWorld();&lt;br /&gt;
&lt;br /&gt;
    // Loop over the cube in the x dimension.&lt;br /&gt;
    for (int xPoint = x1; xPoint &amp;lt;= x2; xPoint++) { &lt;br /&gt;
        // Loop over the cube in the y dimension.&lt;br /&gt;
        for (int yPoint = y1; yPoint &amp;lt;= y2; yPoint++) {&lt;br /&gt;
            // Loop over the cube in the z dimension.&lt;br /&gt;
            for (int zPoint = z1; zPoint &amp;lt;= z2; zPoint++) {&lt;br /&gt;
                // Get the block that we are currently looping over.&lt;br /&gt;
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);&lt;br /&gt;
                // 设置方块ID为57号(钻石块!)&lt;br /&gt;
                currentBlock.setType(Material.DIAMOND_BLOCK); //这里下面会提到&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
这个方法将会在程序给予这个方法高度和起始坐标（最中间的方块的坐标）时，构建一个3D的方块建筑。简单的删除方块也可以使用这个方法的思路实现，但是，但是设置方块的时候，记得把上面标注会提到的那行，设置为物品ID为0的方块(空气方块)。&amp;lt;br&amp;gt; &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== (玩家)操控背包  ==&lt;br /&gt;
&lt;br /&gt;
这个部分大部分讲的是操控玩家的背包。但是在某些情况下，操控箱子里的内容与此相同，当然得在你能找到获取箱子里内容的方法才可以。&amp;amp;nbsp;:P. 这是一个操控玩家背包的一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void onPlayerJoin(PlayerJoinEvent evt) {&lt;br /&gt;
    Player player = evt.getPlayer(); // 当玩家加入游戏&lt;br /&gt;
    PlayerInventory inventory = player.getInventory(); // 获取玩家背包列表&lt;br /&gt;
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // 生成一组钻石&lt;br /&gt;
        &lt;br /&gt;
    if (inventory.contains(itemstack)) {&lt;br /&gt;
        inventory.addItem(itemstack); // 将一组钻石放到玩家的背包里&lt;br /&gt;
        player.sendMessage(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;); //向玩家发送消息(&amp;quot;Wow！你看上去很土豪啊！&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
所以，上面代码里面的 onPlayerJoin 方法中，我们首先构建了一些变量，让我们的工作变得更容易一些。变量分别是： player, inventory and itemstack。 inventory 变量是玩家的背包内容，itemstack是那64个钻石的物品栈。然后我们判断了玩家是不是有一组钻石。如果有，我们就用inventory.addItem(itemstack)方法再给玩家一组钻石，并且给他发送一个消息“Wow！你看上去很土豪啊！”。 所以操控背包并不像我们想象的那么难，如果我们想实现让玩家进入游戏，检测到一组钻石后删掉这组钻石，只需要把代码里的inventory.addItem(itemstack)替换成inventory.remove(itemstack)。获取你还得把“Wow！你看上去很土豪啊！”改成“Wow！你看上去好穷啊！”2333333。&lt;br /&gt;
&lt;br /&gt;
== Item Manipulation ==&lt;br /&gt;
&lt;br /&gt;
When dealing with items in the code, you use the ItemStack class for looking up and setting all information on that stack.&lt;br /&gt;
&lt;br /&gt;
=== Enchantments ===&lt;br /&gt;
&lt;br /&gt;
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&#039;t work) because they&#039;re abstract, so you must use an EnchantmentWrapper. If you want to enchant items that can&#039;t be enchanted inside normal SMP, use addUnsafeEnchantment() instead of addEnchantment()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
int itemCode = 280;  //use the item code you want here&lt;br /&gt;
int effectId = 20;  //use the enchantment code you want here&lt;br /&gt;
int enchantmentLevel = 100;&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(itemCode);  //new item of item code&lt;br /&gt;
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id&lt;br /&gt;
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //enchant the item&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ItemMeta ===&lt;br /&gt;
You can set the display name of an item by doing this.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
String myDisplayName = &amp;quot;Awesome Sword&amp;quot;; //这里是保存了你想要显示的名字&lt;br /&gt;
 &lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你要更改的方块&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //得到方块的元数据&lt;br /&gt;
im.setDisplayName(myDisplayName); //设置它显示的名字&lt;br /&gt;
myItem.setItemMeta(im); //保存更改&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
You can also set the lores of an item. The lores are the small annotations on an item, like &amp;quot;+5 attack damage&amp;quot; on a stone sword.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;String&amp;gt; lores = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
lores.add(&amp;quot;Example lore&amp;quot;, &amp;quot;this one comes on line 2&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again&lt;br /&gt;
im.setLore(lores); //add the lores of course&lt;br /&gt;
myItem.setItemMeta(im); //give the item the new itemmeta&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maps, and Sets, and Lists, Oh My! &amp;lt;ref&amp;gt;标题中Map,Set,List均为Java类集中的接口，故不翻译&amp;lt;/ref&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
除了Map类和HashMap类，Java中还提供了其它的数据结构类集。 &lt;br /&gt;
&lt;br /&gt;
他们提供这些不同的类，是因为有些时候，使用Map类并不合适。&amp;lt;nowiki&amp;gt;[[Java data structure classes]]&amp;lt;/nowiki&amp;gt;在这个条目中，有对Java数据框架类集更深入的讲述。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;=== HashMaps概览以及如何使用此接口  ===&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;Keep in mind to never use a player in a hashmap!&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;&amp;lt;ref&amp;gt;下面有讲解。&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
应在HashMap中使用字符串.，使用 &amp;quot;p.getName()&amp;quot; 来添加，移除或是检查列表中是否含有某个玩家.&lt;br /&gt;
&lt;br /&gt;
将玩家当作object储存在HashMap中会造成大量内存泄漏.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;adam&amp;quot; as the key and assigned a value of &amp;quot;a&amp;quot; to it. That would work as intended, but then say afterwards I wanted to assign the value of &amp;quot;b&amp;quot; to key &amp;quot;adam&amp;quot; I would be able to and would get no errors but the value of &amp;quot;a&amp;quot; assigned to key &amp;quot;adam&amp;quot; in the HashMap would be overwritten because HashMaps cannot contain duplicate values. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Defining a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public Map&amp;lt;KeyType, DataType&amp;gt; HashMapName = new HashMap&amp;lt;&amp;gt;(); //Example syntax&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Example Declaration&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; pluginEnabled = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; isGodMode = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So inside on command you&#039;ll need this, the function name can be different but for the sake of simplicity it&#039;s best if you keep it the same. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;Player player = (Player) sender;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
togglePluginState(player);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void togglePluginState(Player player) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Notice how we use the player name as the key here,&lt;br /&gt;
&lt;br /&gt;
    // not the player object&lt;br /&gt;
&lt;br /&gt;
    String playerName = player.getName();&lt;br /&gt;
&lt;br /&gt;
    if (pluginEnabled.containsKey(playerName)) {&lt;br /&gt;
&lt;br /&gt;
        if (pluginEnabled.get(playerName)) {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, false);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin disabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, true);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    } else {&lt;br /&gt;
&lt;br /&gt;
        pluginEnabled.put(playerName, true); //If you want plugin disabled by default change this value to false.&lt;br /&gt;
&lt;br /&gt;
        player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== More Ideas for HashMaps  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A HashMap (or really any kind of Map in Java) is an association. It allows quick and efficient lookup of some sort of &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;value&#039;&#039;&#039;, given a unique &#039;&#039;&#039;key&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;. Anywhere this happens in your code, a Map may be your solution. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here are a few other ideas which are ideally suited to using Maps. As you will see, it doesn&#039;t have to be data that you store per player, but can be any kind of data that needs to be &amp;quot;translated&amp;quot; from one form to another. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Data Value Lookups  =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Integer&amp;gt; wool_colors = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;orange&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;magenta&amp;quot;, 2);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;light blue&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
   ..&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;black&amp;quot;, 15);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this in response to user commands - turn &amp;quot;green&amp;quot; into 13&lt;br /&gt;
&lt;br /&gt;
int datavalue = 0;&lt;br /&gt;
&lt;br /&gt;
if (wool_colors.containsKey(argument)) {&lt;br /&gt;
&lt;br /&gt;
    datavalue = wool_colors.get(argument);&lt;br /&gt;
&lt;br /&gt;
} else {&lt;br /&gt;
&lt;br /&gt;
    try { datavalue = Integer.parseInt(argument); }&lt;br /&gt;
&lt;br /&gt;
    catch (Exception e) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Saving/Loading a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you don&#039;t want an administrator to edit the data manually&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you need to save data in binary format (too complex to organize for YAML)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you want to avoid parsing block names and/or other objects from freeform text&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is very simple way how to save any HashMap. You can replace HashMap&amp;amp;amp;lt;String, Integer&amp;amp;amp;gt; with any type of HashMap you want. Let&#039;s take an example HashMap with code to save it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        HashMap&amp;lt;String, Integer&amp;gt; mapToSave = new HashMap&amp;lt;String,Integer&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
        public void save(HashMap&amp;lt;String, Integer&amp;gt; map, String path) {&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(map);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
//Handle I/O exceptions&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
save(mapToSave, getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see it&#039;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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public HashMap&amp;lt;String, Integer&amp;gt; load(String path) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
Object result = ois.readObject();&lt;br /&gt;
&lt;br /&gt;
//you can feel free to cast result to HashMap&amp;lt;String, Integer&amp;gt; if you know there&#039;s that HashMap in the file&lt;br /&gt;
&lt;br /&gt;
return (HashMap&amp;lt;String, Integer&amp;gt;)result;&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HashMap&amp;lt;Integer, String&amp;gt; loadedMap;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
String path = getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
File file = new File(path);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if (file.exists()) { // check if file exists before loading to avoid errors!&lt;br /&gt;
&lt;br /&gt;
loadedMap  = load(path);&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
You can use this &amp;quot;API&amp;quot; for saving/loading HashMaps, ArrayLists, and all Objects which implement Serializable or Externalizable interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;/** SLAPI = Saving/Loading API&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 * API for Saving and Loading Objects.&lt;br /&gt;
&lt;br /&gt;
 * Everyone has permission to include this code in their plugins as they wish :)&lt;br /&gt;
&lt;br /&gt;
 * @author Tomsik68&amp;lt;tomsik68@gmail.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
public class SLAPI&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; void save(T obj,String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(obj);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; T load(String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
T result = (T)ois.readObject();&lt;br /&gt;
&lt;br /&gt;
ois.close();&lt;br /&gt;
&lt;br /&gt;
return result;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Example implementation of this API: &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;I&#039;m skipping some part of code in this source&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public class Example extends JavaPlugin {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
private ArrayList&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onEnable() {&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
list = SLAPI.load(&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                //handle the exception&lt;br /&gt;
&lt;br /&gt;
                e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onDisable()	{&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
SLAPI.save(list,&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                 e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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&#039;re writing your own data object classes, and you may want to save their state using this technique, you should read about Java&#039;s Serializable or Externalizable interface. The only difference between Externalizable and Serializable is, that Serializable automatically takes all of class&#039;s fields and tries to serialize them, while Externalizable allows you to define method for reading and writing the Object. It&#039;s easy to add to your code, and it will make your data persistent with very little work required. No more parsing!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note #2: This API doesn&#039;t support changes. Once you change something in the class, data files saved with older version of your plugin won&#039;t load correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Tips &amp;amp; Examples =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.) Simplify your save structure&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.) Save version number along with data&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always remember, that you don&#039;t know what you&#039;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&#039;t know!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.) Migrate older files&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your plugin finds older version of some file, it should update the file accordingly and change version number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Metadata  ==&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;fields&amp;quot; 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&#039;re creating an economy plugin, you would need a HashMap of Player and Float or Double. With Metadata, you don&#039;t have to! You just attach to player new metadata value, and that&#039;s it!&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== Why to use Metadata  ===&lt;br /&gt;
&lt;br /&gt;
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps. &lt;br /&gt;
* Metadata can be used to share information between plugins.&lt;br /&gt;
&lt;br /&gt;
=== Why not use Metadata&amp;lt;br&amp;gt;  ===&lt;br /&gt;
&lt;br /&gt;
* Slightly more difficult to get the value.&lt;br /&gt;
* It is not saved on shutdown (but then again, neither are any Maps that you create).&lt;br /&gt;
&lt;br /&gt;
=== Getting &amp;amp;amp; Setting Metadata  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {&lt;br /&gt;
  object.setMetadata(key, new FixedMetadataValue(plugin,value));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Object getMetadata(Metadatable object, String key, Plugin plugin) {&lt;br /&gt;
  List&amp;lt;MetadataValue&amp;gt; values = object.getMetadata(key);  &lt;br /&gt;
  for (MetadataValue value : values) {&lt;br /&gt;
     // Plugins are singleton objects, so using == is safe here&lt;br /&gt;
     if (value.getOwningPlugin() == plugin) {&lt;br /&gt;
        return value.value();&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
  return null;&lt;br /&gt;
}&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note: If you&#039;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.&lt;br /&gt;
&lt;br /&gt;
== Databases  ==&lt;br /&gt;
&lt;br /&gt;
Sometimes flat files aren&#039;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).&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there&#039;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!) &lt;br /&gt;
&lt;br /&gt;
=== SQLite  ===&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;s very straightforward and shouldn&#039;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]. &lt;br /&gt;
&lt;br /&gt;
SQLite is great for very simple databases, because there&#039;s no server concerns to set up. Just make a few calls to create a new database and table. It&#039;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&#039;s often easiest and fastest to get the SQL basics squared away with SQLite, even if you &amp;quot;graduate&amp;quot; to a more server-class database engine later.&lt;br /&gt;
&lt;br /&gt;
=== MySQL  ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 部署你的插件  ==&lt;br /&gt;
&lt;br /&gt;
 总之你写好的你的插件,如何把它变成一个可安装在服务端的jar文件呢?&lt;br /&gt;
 &lt;br /&gt;
 首先我们需要建立一个水桶服务器。 [http://wiki.bukkit.org/Setting_up_a_server 在这里] 能找到开服相关信息.接下来你需要把插件导出为一个.jar文件，&lt;br /&gt;
 &lt;br /&gt;
 这样你就能在你的服务器里运行它。 在Eclipse里, 右键工程，点击&#039;&#039;Run as &amp;gt; Maven install&#039;&#039;即可:&lt;br /&gt;
&lt;br /&gt;
[[Image:Maveninstall.png]]&lt;br /&gt;
&lt;br /&gt;
 将来呢, 当你对插件进行了改动后,在做上述操作之前，如果你想删除之前的.jar文件，你可以右键工程，点击 &#039;&#039;Run as &amp;gt; Maven clean&#039;&#039; 。 &lt;br /&gt;
 &lt;br /&gt;
 如果你在编译插件时遇到了问题,  请检查你的JDK是否正确安装， 并浏览[[建立工作区]]条目。&lt;br /&gt;
 &lt;br /&gt;
 如果你在Eclipse控制台看到了和JDK有关的错误，你可能需要手动调整JDK,因为Eclipse的检测可能发生了错误。 &lt;br /&gt;
 &lt;br /&gt;
 点击&#039;&#039;Window -&amp;gt; Preferences&#039;&#039;, 然后是&#039;&#039;Java -&amp;gt; Installed JREs&#039;&#039;。选中你最近安装的JDK作为Java运行环境（JRE）并添加, 并取消掉原来那个报错的JDK:&lt;br /&gt;
&lt;br /&gt;
[[Image:Jrelocation.png]]&lt;br /&gt;
&lt;br /&gt;
 如果你的工程成功被编译,JAR文件会生产在 &#039;&#039;target&#039;&#039; 目录下，具体在Eclipse的工作空间里名字为工程名的文件夹中。&lt;br /&gt;
 &lt;br /&gt;
 这个JAR文件就是一个能正常工作的Bukkit插件了。当然前提是你的plugin.yml里没出差错。&lt;br /&gt;
 &lt;br /&gt;
 你可以把这个jar文件丢到服务器的plugins文件夹里,重载或重启服务器,就可以测试你的新插件啦。&lt;br /&gt;
 &lt;br /&gt;
 为了连接上使用你自己的电脑开的服务器 ,在多人游戏的ip里填上你的本地ip即可。&lt;br /&gt;
 &lt;br /&gt;
 如果出现了错误而你又不能解决的话, 试试这里[http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], &lt;br /&gt;
 &lt;br /&gt;
 在这里提问[http://wiki.bukkit.org/IRC bukkitdev IRC channel],或者重新阅读wiki。&lt;br /&gt;
 &lt;br /&gt;
 当你做完了一个实用的插件,可以考虑把它发表在这里[http://dev.bukkit.org/ dev.bukkit]（为了Bukkit社区的访问量）。&lt;br /&gt;
 &lt;br /&gt;
 以上可知,JAR格式是默认压缩生成的(JAR文件基于ZIP文件的格式)。这么说来，在上传插件到Bukkit上时把JAR文件压缩成ZIP只会增加文件体积罢了。&lt;br /&gt;
 &lt;br /&gt;
 而且呢, 放在JAR中的config文件可以在检测不到插件目录（插件自动生成的文件夹，名字与插件名相同，里面是该插件的配置）下的config文件时自动生成。&lt;br /&gt;
 &lt;br /&gt;
 所以我们一般不把jar文件转换成其他格式。&lt;br /&gt;
&lt;br /&gt;
== 导入其它插件 ==&lt;br /&gt;
你可能会希望编辑其它那些有源代码的插件. 如果插件在它的目录下有&#039;&#039;pom.xml&#039;&#039;(大多数知名插件,例如WorldEdit和Essentials都有),你可以通过点击&#039;&#039;File -&amp;gt; Import&#039;&#039;来把它作为一个工程导入, 然后打开&#039;&#039;Maven&#039;&#039;项并选择&#039;&#039;Existing Maven Projects&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
[[Image:Importmaven.png]]&lt;br /&gt;
&lt;br /&gt;
然后选择&#039;&#039;pom.xml&#039;&#039;所在的目录,这个工程就应该出现在你的侧边栏里了。就像平常一样编辑并编译它吧。&lt;br /&gt;
&lt;br /&gt;
== 小贴士  ==&lt;br /&gt;
&lt;br /&gt;
=== 使玩家着火 ===&lt;br /&gt;
&lt;br /&gt;
 Bukkit API有很多碉堡的功能。以下是实现一些功能的代码片段。 &lt;br /&gt;
&lt;br /&gt;
下面的代码运行一个玩家使另一个玩家着火。通过类似于&#039;&#039;&#039;/ignite Notch&#039;&#039;&#039;这样的指令，这个指令会让 Notch 着火。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     // 使用 equalsIgnoreCase() 来忽视大小写&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;ignite&amp;quot;)) {&lt;br /&gt;
         // 确保玩家只输入了一个自变量(要被点着的玩家名).&lt;br /&gt;
         if (args.length != 1) {&lt;br /&gt;
             // 当return false时，该指令的使用信息会显示出来。&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 确保命令发出者是个玩家.&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can set other players on fire.&amp;quot;);&lt;br /&gt;
             sender.sendMessage(&amp;quot;This is an arbitrary requirement for demonstration purposes only.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 获取那个要被点着的玩家. 请注意下标起始是0，不是1.&lt;br /&gt;
         Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
 &lt;br /&gt;
         // 确保这个玩家在线。&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 让这个玩家着火1000 ticks (一秒钟大概有20ticks,所以总共是50秒).&lt;br /&gt;
         target.setFireTicks(1000);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 干掉玩家  ===&lt;br /&gt;
&lt;br /&gt;
 和标题一样.这里是要干掉某个玩家。 &lt;br /&gt;
&lt;br /&gt;
在你的onCommand方法里使用这个: &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;KillPlayer&amp;quot;)) {&lt;br /&gt;
         Player target = sender.getServer().getPlayer(args[0]);&lt;br /&gt;
          // 确保玩家在线.&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         target.setHealth(0); &lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这里有个小扩展,会使用一个爆炸来杀死玩家: &lt;br /&gt;
&lt;br /&gt;
 float explosionPower = 4F; //爆炸等级 - TNT的爆炸等级默认是4F&lt;br /&gt;
 Player target = sender.getWorld().getPlayer(args[0]);&lt;br /&gt;
 target.getWorld().createExplosion(target.getLocation(), explosionPower);&lt;br /&gt;
 target.setHealth(0);&lt;br /&gt;
&lt;br /&gt;
=== 创造一个假爆炸  ===&lt;br /&gt;
&lt;br /&gt;
这段代码会生产一个看起来和听起来与TNT/苦力怕产生的爆炸一样的效果。但是不会对实体和方块产生破坏。 在保留爆炸的艺术的同时又削弱了爆炸的威力，真是太有用了（&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onExplosionPrime(ExplosionPrimeEvent event) {	&lt;br /&gt;
     Entity entity = event.getEntity();&lt;br /&gt;
 &lt;br /&gt;
     // 如果事件和点燃的TNT有关,执行一些代码（又略）&lt;br /&gt;
     if (entity instanceof TNTPrimed) {&lt;br /&gt;
         entity.getWorld().createExplosion(entity.getLocation(), 0);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 使一个玩家对另外一个玩家隐形  ===&lt;br /&gt;
&lt;br /&gt;
 通过输入指令，能让指令执行者对特定的玩家隐形。 然而其他人仍然能看到指令执行者。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;HideMe&amp;quot;) &amp;amp;&amp;amp; args.length == 1) {&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can use this command!&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         // 检测指令执行者是玩家后，我们便可以安全的进行强制转换类操作。&lt;br /&gt;
        Player s = (Player) sender;&lt;br /&gt;
 &lt;br /&gt;
        // 获取命令执行者指定的玩家&lt;br /&gt;
        Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
        if (target == null) {&lt;br /&gt;
            sender.sendMessage(&amp;quot;Player &amp;quot; + args[0] + &amp;quot; is not online.&amp;quot;);&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        // 将 s 从 target 的视野中隐藏起来.&lt;br /&gt;
        target.hidePlayer(s);&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 在玩家准星所指处生产闪电 ===&lt;br /&gt;
&lt;br /&gt;
如下代码运行手持鱼竿的玩家通过点击来生成闪电 (当然要用准星瞄准)。 真是个简单好玩的恶作剧（。&lt;br /&gt;
&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onPlayerInteractBlock(PlayerInteractEvent event) {&lt;br /&gt;
     Player player = event.getPlayer();&lt;br /&gt;
     if (player.getItemInHand().getType() == Material.FISHING_ROD) {&lt;br /&gt;
         // 在给定坐标中生成一道闪电. 在本例中, 这个坐标是玩家准星瞄准的地方.&lt;br /&gt;
         // 只能指向200格以内的坐标.&lt;br /&gt;
         player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 自动格式 ===&lt;br /&gt;
&lt;br /&gt;
 Eclipse提供了将你的代码自动转成Oracle规定的格式, 修正了不符合常规的缩进, 空格等等。 在侧边栏选中你的项目,然后选择&#039;&#039;Source -&amp;gt; Format&#039;&#039;即可对其进行更改。&lt;br /&gt;
&lt;br /&gt;
== 插件需求专区  ==&lt;br /&gt;
&lt;br /&gt;
http://forums.bukkit.org/forums/plugin-requests.13/&lt;br /&gt;
&lt;br /&gt;
== 范例文件和样板  ==&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/wpeTPx7N Example.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have any more questions on this matter, don&#039;t hesitate to visit the BukkitDev [[IRC]] channel and ask!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:教程]]&lt;br /&gt;
[[Category:大篇幅条目]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
	<entry>
		<id>https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1341</id>
		<title>Bukkit/插件开发教程</title>
		<link rel="alternate" type="text/html" href="https://mineplugin.org/index.php?title=Bukkit/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&amp;diff=1341"/>
		<updated>2015-08-31T02:23:13Z</updated>

		<summary type="html">&lt;p&gt;Tdiant：​/* 方块操控 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{模板:待翻译}}&lt;br /&gt;
{{模板:待完善}}&lt;br /&gt;
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]&lt;br /&gt;
== 介绍  ==&lt;br /&gt;
&lt;br /&gt;
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.&lt;br /&gt;
&lt;br /&gt;
== 学习Java  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;理解这些教程需要具备Java这门编程语言的基本知识.&#039;&#039;&#039;如果你刚开始学习Java或者需要重温一下相关知识，下面有一个并不完整的网站列表可供参考.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;[http://docs.oracle.com/javase/tutorial/ Oracle&#039;s Java Tutorials]&amp;lt;/big&amp;gt; - 官方教程&lt;br /&gt;
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程&lt;br /&gt;
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程&lt;br /&gt;
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书&lt;br /&gt;
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.&lt;br /&gt;
&lt;br /&gt;
== 开发环境  ==&lt;br /&gt;
&lt;br /&gt;
在编写一个插件前 (或学习Java前) 你需要配置一个开发者环境，这个包括但不限于安装IDE (Integrated Development Environment，集成开发环境)。接下来的教程中有关于Eclipse上IDE的操作指南。&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;更多信息，请参见 [[Bukkit/插件开发教程-建立工作区|建立工作区]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
你&#039;&#039;&#039;必须&#039;&#039;&#039;下载&#039;&#039;&#039;Java&#039;&#039;&#039;开发者使用的Eclipse构建版本, &#039;&#039;&#039;不是&#039;&#039;&#039; &#039;&#039;&#039;Java EE&#039;&#039;&#039;开发者的版本。 Java EE 开发者的版本不包含此教程需要的Maven支持。&lt;br /&gt;
&lt;br /&gt;
== 新建插件开发项目  ==&lt;br /&gt;
=== 创建一个新的项目 ===&lt;br /&gt;
&lt;br /&gt;
在开始工作之前，你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击&#039;&#039;File -&amp;gt; New -&amp;gt; Project:&#039;&#039;来创建一个新的项目.&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject.png]] &lt;br /&gt;
&lt;br /&gt;
现在,打开&#039;&#039;Maven&#039;&#039;文件夹, 然后选择&#039;&#039;Maven Project&#039;&#039;.点击next,之后在下一个菜单中选择&#039;&#039;Create a simple project&#039;&#039;, 再次点击&#039;&#039;Next&#039;&#039;:&lt;br /&gt;
如果你看不到&#039;&#039;Maven&#039;&#039;文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject2.png]] &lt;br /&gt;
&lt;br /&gt;
现在，你需要给你的组用户命名，就像下面这样:&lt;br /&gt;
* 如果你拥有一个域名，package则填写逆转的域名地址. &lt;br /&gt;
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]&lt;br /&gt;
** 避免使用一个不属于你自己的域名.&lt;br /&gt;
* 没有域名? 这里有几种方法可供选择。&lt;br /&gt;
*# 在资源管理站点创建一个用户，比如GitHub或是sourceforge&lt;br /&gt;
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 使用你的邮箱.  例如: &amp;lt;username&amp;gt;@gmail.com格式的邮箱应输入com.gmail.&amp;lt;username&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
*# 最不济的方法.：  使用独特的组名命名方式，这应当是你最后的解决方法。&lt;br /&gt;
&lt;br /&gt;
以下几个地址不能作为package中的地址前缀:&lt;br /&gt;
* org.bukkit&lt;br /&gt;
* net.bukkit&lt;br /&gt;
* com.bukkit&lt;br /&gt;
* net.minecraft&lt;br /&gt;
&lt;br /&gt;
完成基础的组名以后，你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 &#039;&#039;TestPlugin&#039;&#039;的插件，那么完整的组名是&#039;&#039;io.github.&amp;lt;username&amp;gt;&#039;&#039;, 你的工程名也是 &#039;&#039;TestPlugin&#039;&#039;. 至于版本，默认即可，稍后可以修改。&lt;br /&gt;
完成向导:&lt;br /&gt;
&lt;br /&gt;
[[Image:Newproject3.png]] &lt;br /&gt;
&lt;br /&gt;
如果这是你首次使用Eclipse, 点击右上角的&amp;quot;X&amp;quot; 来关闭Welcome提示页面. 现在，你的窗口视图看起来就像下面的图片这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Eclipsemain.png]] &lt;br /&gt;
&lt;br /&gt;
点击工程名称右边的箭头来进行下一步，现在我们正式开始。&lt;br /&gt;
=== 添加Bukkit API ===&lt;br /&gt;
&lt;br /&gt;
在开发插件之前，你需要添加 Bukkit API库文件到你的项目，作为一个dependency, 你也可以添加其他你想实用的API.&lt;br /&gt;
&lt;br /&gt;
找到项目文件夹中部的&#039;&#039;pom.xml&#039;&#039;并双击进行编辑. 点击&#039;&#039;pom.xml&#039;&#039;中部的 tab,你将会看到下图所示内容:&lt;br /&gt;
&lt;br /&gt;
[[Image:Pomeditor.png]]&lt;br /&gt;
&lt;br /&gt;
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;build&amp;gt;&lt;br /&gt;
       &amp;lt;plugins&amp;gt;&lt;br /&gt;
           &amp;lt;plugin&amp;gt;&lt;br /&gt;
               &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;
               &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
               &amp;lt;configuration&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;amp;lt;&amp;lt;/nowiki&amp;gt;source&amp;gt;1.7&amp;lt;/source&amp;gt;&lt;br /&gt;
                   &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;&lt;br /&gt;
               &amp;lt;/configuration&amp;gt;&lt;br /&gt;
           &amp;lt;/plugin&amp;gt;&lt;br /&gt;
       &amp;lt;/plugins&amp;gt;&lt;br /&gt;
    &amp;lt;/build&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你或许想使用其他版本,例如 &amp;lt;code&amp;gt;1.8&amp;lt;/code&amp;gt; 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码&amp;quot;error&amp;quot;中建议你更改语言版本.同意即可.&lt;br /&gt;
&lt;br /&gt;
在位于代码中段的&#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit&#039;s repository的地址): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;repositories&amp;gt;&lt;br /&gt;
        &amp;lt;repository&amp;gt;&lt;br /&gt;
            &amp;lt;id&amp;gt;bukkit-repo&amp;lt;/id&amp;gt;&lt;br /&gt;
            &amp;lt;url&amp;gt;http://repo.bukkit.org/content/groups/public/&amp;lt;/url&amp;gt;&lt;br /&gt;
        &amp;lt;/repository&amp;gt;&lt;br /&gt;
    &amp;lt;/repositories&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后继续在 &#039;&#039;&amp;lt;/project&amp;gt;&#039;&#039;前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;lt;dependencies&amp;gt;&lt;br /&gt;
        &amp;lt;dependency&amp;gt;&lt;br /&gt;
            &amp;lt;groupId&amp;gt;org.bukkit&amp;lt;/groupId&amp;gt;&lt;br /&gt;
            &amp;lt;artifactId&amp;gt;bukkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
            &amp;lt;version&amp;gt;1.7.2-R0.3&amp;lt;/version&amp;gt;&lt;br /&gt;
            &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;
            &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;
        &amp;lt;/dependency&amp;gt;&lt;br /&gt;
    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.&lt;br /&gt;
&lt;br /&gt;
当你完成上述步骤后,&#039;&#039;pom.xml&#039;&#039;中的内容应该是这样:&lt;br /&gt;
&lt;br /&gt;
[[Image:Finishedpom.png]]&lt;br /&gt;
&lt;br /&gt;
通过菜单 &#039;&#039;File -&amp;gt; Save&#039;&#039;或者按住 &amp;lt;code&amp;gt;Ctrl + S&amp;lt;/code&amp;gt; 来保存&#039;&#039;pom.xml&#039;&#039;文件 .  之后, 右键项目标题，然后依次选择 &#039;&#039;Maven -&amp;gt; Update Project&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Bukkit Java文档 ===&lt;br /&gt;
&lt;br /&gt;
 如果你曾经使用过Eclipse和Java，你会知道当你将鼠标准心移至class或者 method中的代码部分时，一个含有相关文档内容的黄色小框会弹出来. 这就是（Java的）Java文档。&lt;br /&gt;
 &lt;br /&gt;
 你可以[http://download.oracle.com/javase/6/docs/api/ Oracle website 在线获取]它.。Bukkit同样拥有文档，这些文档内容包含了关于API中method和class的有用描述。&lt;br /&gt;
 &lt;br /&gt;
 你可以在[http://jd.bukkit.org/ 这]找到它 (Beta版的Java文档在[http://jd.bukkit.org/ 这]找, 新开发出来的Java文档在  &lt;br /&gt;
 &lt;br /&gt;
 [http://jd.bukkit.org/ 这]) 。为了在Eclipse里使用Java文档,这样的话当你的鼠标停留在Bukkit的class和method上时，&lt;br /&gt;
 &lt;br /&gt;
 （Bukkit的）Java文档就会冒出来。  &lt;br /&gt;
JavaDoc in spigot [https://hub.spigotmc.org/javadocs/bukkit/ Click Here]&lt;br /&gt;
&lt;br /&gt;
 首先右键在侧边栏里的&amp;quot;Maven Dependencies&amp;quot;下面的Bukkit jar,选择&amp;quot;Properties&amp;quot;.选中窗口左边的&#039;&#039;Javadoc Location&#039;&#039;, 并粘贴如下网址 &lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;http://jd.bukkit.org/&#039;&#039;&#039; (或者上面的测试版/最新版 Java文档链接也好) 到&amp;quot;Javadoc URL&amp;quot;下面的输入框内. 就像这样: &lt;br /&gt;
&lt;br /&gt;
[[Image:Bukkitjavadocs.png]] &lt;br /&gt;
&lt;br /&gt;
 点击 &amp;quot;Validate&amp;quot;,然后再点击&amp;quot;OK&amp;quot;就完成了.现在Bukkit Javadocs已经连接到Bukkit提供的资源,同时你也可以通过Eclipse来获得帮助类文档信息了.&lt;br /&gt;
&lt;br /&gt;
=== 创建一个包 ===&lt;br /&gt;
&lt;br /&gt;
 现在你需要创建一个包，它将储存所有我们需要使用的Java类文件. 右键展开&#039;&#039;src/main/java&#039;&#039;折叠并选择 &#039;&#039;New &amp;amp;gt; Package&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
[[Image:Newpackage.png]] &lt;br /&gt;
&lt;br /&gt;
 包名的话，先写上你的组名，然后加个点，再写上你的小写Artifact ID。举例, 如果你的组名是&#039;&#039;io.github.name&#039;&#039;，你的artifact名是 &#039;&#039;TestPlugin&#039;&#039;。&lt;br /&gt;
 &lt;br /&gt;
 你的包名就应该是&#039;&#039;io.github.name.testplugin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== 创建插件的类文件 ===&lt;br /&gt;
&lt;br /&gt;
既然我们已经建立好了我们的项目，我们接下来可以开始添加类文件以及制作插件了. 插件的主类指的是拓展(extends)JavaPlugin的类文件. 在你的插件中只能有一个类文件拓展JavaPlugin，无论直接还是间接. 先创建你的主类文件并保持名称与插件名一致是个很好的习惯.右键你之前新建的包,并选择&amp;amp;nbsp; &#039;&#039;New &amp;amp;gt; Class&#039;&#039;.你将会创建一个新的类文件，就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$GroupName}.{$ArtifactName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$ArtifactName} extends JavaPlugin {&lt;br /&gt;
    &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
警告： 插件绝对不应该调用自己的构造函数并实例化&lt;br /&gt;
&lt;br /&gt;
=== 创建plugin.yml ===&lt;br /&gt;
&lt;br /&gt;
你已经创建了项目和主类，如果你想要Bukkit能够加载你的插件，你还必须创建 &#039;&#039;&#039;[[Plugin YAML|plugin.yml]]&#039;&#039;&#039; 文件. 这个文件包含有基础的插件信息，如果缺失这个文件，你的插件也将会失效. 此时你需要右键&#039;&#039;src/main/resources&#039;&#039;. 选择&#039;&#039;New &amp;amp;gt; File&#039;&#039;. 命名为&amp;quot;&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;&amp;quot; 并右键完成新文件的创建.Eclipse会打开默认的文本编辑窗口来显示&#039;&#039;&#039;plugin.yml&#039;&#039;&#039;文件中空白的内容并提供编辑. &#039;&#039;(Hint:&amp;amp;nbsp;如果你想要使得你的工作空间更加规整，关闭文本编辑器并将&#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件拖到主工作空间(拖到文件目录的右边) 之后你就可以在eclipse中编辑文件了.)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
有三个基础的内容需要在plugin.yml写明. &lt;br /&gt;
: name:  插件名称. &lt;br /&gt;
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 插件主类的完整，合法名称] . &lt;br /&gt;
: version: 插件当前版本号. &lt;br /&gt;
&lt;br /&gt;
最简单的 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件内就像这样&amp;amp;nbsp;: &lt;br /&gt;
&lt;br /&gt;
 name: {插件名称}&lt;br /&gt;
 main: {包名}.{主类}&lt;br /&gt;
 version: {版本号}&lt;br /&gt;
&lt;br /&gt;
 PS:插件的包名经常会包括插件的名称，所以看到这个的时候不要感到惊讶。&lt;br /&gt;
    你的主类是否是你的插件名取决于你之前的命名方式，时刻记住这一点很重要。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;更多例子&#039;&#039;&#039;, 请看[[#Example_Files_and_Templates]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
此时你的插件已经可以被Bukkit加载了,同时服务端日志也会开始记录你的插件. 不过它现在什么用都没有!&lt;br /&gt;
&lt;br /&gt;
== onEnable()和onDisable()方法  ==&lt;br /&gt;
&lt;br /&gt;
这些方法将在插件启用与卸载时生效. 默认情况下，你的插件在被加载时会调用这些方法，所以你可以在这里注册你需要用到的事件和提供一些调试信息. &amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 方法会在插件启用时被调用, 需要包含初始化插件的逻辑语句. &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法会在插件卸载时被调用，需要包含清理(clean up)插件的逻辑语句和连接声明(associated state)。此外，插件可以重写 &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; 方法来在插件载入时执行附加的逻辑语句。&lt;br /&gt;
&lt;br /&gt;
=== onEnable()和onDisable()方法介绍  ===&lt;br /&gt;
&lt;br /&gt;
在前面的章节中，我们在主类创建了&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;onDisable()&amp;lt;/code&amp;gt; 方法. 此时，这些代码看起来就像下面这样&lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
     @Override&lt;br /&gt;
     public void onEnable() {&lt;br /&gt;
         // 插件载入时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     @Override&lt;br /&gt;
     public void onDisable() {&lt;br /&gt;
         // 插件卸载时要执行的代码（略）&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这些方法已经创建，但目前它们还没有任何作用.&lt;br /&gt;
注意: 不需要添加代码来实现输出信息&amp;quot;{$PluginName} has been enabled!&amp;quot; ，因为bukkit会自动输出此类信息&lt;br /&gt;
有关更多事件的信息请查看 [[Event_API_Reference|这里]].&lt;br /&gt;
&lt;br /&gt;
=== 发送提示信息 ===&lt;br /&gt;
&lt;br /&gt;
 插件能够输出信息至控制台与服务器日志。这是通过调用插件记录器里正确的方法来实现的。首先我们必须调用 &amp;lt;code&amp;gt;getLogger()&amp;lt;/code&amp;gt; 方法来检索该插件的记录器，&lt;br /&gt;
 &lt;br /&gt;
 然后我们就可以开始记录信息了。&lt;br /&gt;
&lt;br /&gt;
我们通过调用&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法可以实现输出信息。 我们需要在&amp;lt;code&amp;gt;onEnable()&amp;lt;/code&amp;gt;方法下面插入如下代码：&lt;br /&gt;
&lt;br /&gt;
 getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
在 &amp;lt;code&amp;gt;&#039;&#039;&#039;onDisable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法里面，你也可以做类似的事情，不过要改改输出信息。&lt;br /&gt;
&lt;br /&gt;
此时你的主类文件就像这样: &lt;br /&gt;
&lt;br /&gt;
 package {$TopLevelDomain}.{$Domain}.{$PluginName};&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.plugin.java.JavaPlugin;&lt;br /&gt;
 &lt;br /&gt;
 public final class {$PluginName} extends JavaPlugin {&lt;br /&gt;
         @Override&lt;br /&gt;
         public void onEnable() {&lt;br /&gt;
             getLogger().info(&amp;quot;onEnable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public void onDisable() {&lt;br /&gt;
         getLogger().info(&amp;quot;onDisable has been invoked!&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 防止重载后插件失效 ===&lt;br /&gt;
&lt;br /&gt;
你需要知道的是，插件并不只会伴随服务器关闭、启动而重载，你的插件也可能会被其他插件启用、关闭，或是在服务器运行时使用/reload命令. &lt;br /&gt;
假设服务器启动之后一会儿后，你的插件才被启用，这时便是危险的,因为服务器中可能已经有玩家在线、额外的世界被加载以及很多预想不到的不同之处(与&amp;quot;插件同服务器同时加载&amp;quot;的情况相比较). &lt;br /&gt;
&lt;br /&gt;
举个例子来说:&lt;br /&gt;
* 你有一个用来储存HashMap中的玩家登入信息的插件&lt;br /&gt;
* 你希望该信息对所有玩家都可见&lt;br /&gt;
* 一名管理员使用/reload命令&lt;br /&gt;
* 你的插件被卸载，所有储存的数据都丢失了&lt;br /&gt;
* 你的插件被再次启用时，一些玩家已经在线&lt;br /&gt;
* 这些玩家在HashMap并没有储存的信息&lt;br /&gt;
* 你试图重新获得他们的信息，但是失败了!&lt;br /&gt;
&lt;br /&gt;
为了插件重载以后能够正常工作, 你需要在插件启用时加载所有已经在线的玩家的信息并将之储存在HashMap中.&lt;br /&gt;
&lt;br /&gt;
 for (Player player : Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
     playerList.put(player.getName(), playerData(player));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== 监听器  ==&lt;br /&gt;
监听器是一种方法被调用来对事件作出反应的类.所有的监听器使用 org.bukkit.event.Listener接口.更多有关监听器创建的细节,&lt;br /&gt;
:: &#039;&#039; 请查看: [[Event API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 指令  ==&lt;br /&gt;
&lt;br /&gt;
=== onCommand() 方法  ===&lt;br /&gt;
&lt;br /&gt;
 现在你已经知道如何注册一个事件并做出响应,但是如果你只是想要在命令输入之后做出响应呢? 你可以使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 这个代码的作用是当玩家输入“/”时，监听该操作并执行相关语句&amp;quot;/&amp;quot; . 举个例子来说.输入 &amp;quot;/do something&amp;quot; 将会执行 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法.&lt;br /&gt;
 &lt;br /&gt;
 此种情况下，因为没有特定的行为被编程，所以并不会发生任何事情。&lt;br /&gt;
&lt;br /&gt;
 请避免使用和bukkit所提供的指令重名的指令, 然后深思你的指令名的唯一性。 &lt;br /&gt;
 &lt;br /&gt;
 例如.指令&amp;quot;give&amp;quot;已经被好几个插件使用了, 如果你执行了另外一个&amp;quot;give&amp;quot;指令, 你的插件将会和这些插件冲突。&lt;br /&gt;
&lt;br /&gt;
 你必须在插件的&amp;quot;plugins.yml&amp;quot;注册你的指令 否则这个方法将不会被触发。&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法必须返回一个布尔值（true或false）。 &lt;br /&gt;
 &lt;br /&gt;
 如果返回值是true，你不会看到什么明显的事情发生。 &lt;br /&gt;
 &lt;br /&gt;
 但如果返回值是false，则会返回你的plugin.yml里的&#039;usage:property&#039;然后发送给命令使用者. &lt;br /&gt;
&lt;br /&gt;
当使用 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时, 你需要填写4个参数. &lt;br /&gt;
&lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 发送命令的对象&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;Command cmd&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行的指令&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String commandLabel&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被执行指令的别名&lt;br /&gt;
 &lt;br /&gt;
 *&amp;lt;code&amp;gt;&#039;&#039;&#039;String[] args&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 该指令的自变量数组。&lt;br /&gt;
 &lt;br /&gt;
例如.指令 &#039;&#039;/hello abc def&#039;&#039; 中， &#039;&#039;abc&#039;&#039; 会被存放进args[0]中, &#039;&#039;def&#039;&#039; 被存放进args[1]中。&lt;br /&gt;
&lt;br /&gt;
==== 设置命令  ====&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { // 如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
     // 所需要执行的事（此处略）&lt;br /&gt;
         return true;&lt;br /&gt;
     } //如果以上内容成功执行，则返回true &lt;br /&gt;
     // 如果执行失败，则返回false.&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 每当使用&amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法时， 在每个方法后面都加上一个 return false; 是一个惯例。 &lt;br /&gt;
 &lt;br /&gt;
 返回false会显示在plugin.yml里设置的usage信息 (看下面). 用这种方法的话，一旦执行出错便会显示usage信息。&lt;br /&gt;
&lt;br /&gt;
 每当方法return一个值的时候，这个方法就会退出，所以return true的时候，在它下面的代码就会被跳过, &lt;br /&gt;
 &lt;br /&gt;
 除非return语句在一个if的嵌套中，或者类似的嵌套情况。 &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;&#039;&#039;&#039;.equalsIgnoreCase(&amp;quot;basic&amp;quot;)&#039;&#039;&#039;&amp;lt;/code&amp;gt;代表忽略英文大小写. 在这种情况下,字符串&amp;quot;BAsIc&amp;quot; 和 &amp;quot;BasiC&amp;quot;和 basic相同，代码会被照常执行。&lt;br /&gt;
&lt;br /&gt;
和以前一样，加两行import在你的java文件头&lt;br /&gt;
&lt;br /&gt;
 import org.bukkit.command.Command;&lt;br /&gt;
 &lt;br /&gt;
 import org.bukkit.command.CommandSender;&lt;br /&gt;
&lt;br /&gt;
=== 在Plugin.yml中添加你的指令  ===&lt;br /&gt;
&lt;br /&gt;
你需要添加你的指令到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039; 文件里. 如下是指令/basic添加到 &#039;&#039;&#039;plugin.yml&#039;&#039;&#039;的例子（请在plugins.yml的末尾添加如下代码）: &lt;br /&gt;
&lt;br /&gt;
commands:&lt;br /&gt;
   basic:&lt;br /&gt;
      description: This is a demo command.&lt;br /&gt;
      usage: /&amp;lt;command&amp;gt; [player]&lt;br /&gt;
      permission: &amp;lt;plugin name&amp;gt;.basic&lt;br /&gt;
      permission-message: You don&#039;t have &amp;lt;permission&amp;gt;&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;basic&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令名称&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;description&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 指令描述&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;usage&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 在oncommand方法return false后显示的用法提示。尽量简洁, 使别人能够理解指令是什么以及如何使用它。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 被一些显示帮助信息的插件使用，用来决定向玩家显示哪个指令（完全不对劲吧！）。&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;&#039;&#039;permission-message&#039;&#039;&#039;&amp;lt;/code&amp;gt; - 当玩家使用了这个指令而没有权限时输出的信息。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 PS:yml文件使用两个空格作为制表符,使用tab键输入制表符会导致错误。&lt;br /&gt;
&lt;br /&gt;
===控制台指令vs玩家指令===&lt;br /&gt;
&lt;br /&gt;
 你可能注意到了上文的&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender sender&#039;&#039;&#039;&amp;lt;/code&amp;gt;参数. &amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt; 是个Bukkit接口，&lt;br /&gt;
 &lt;br /&gt;
 它有两个（对插件编写者）有用的子类：&amp;lt;code&amp;gt;&#039;&#039;&#039;Player&#039;&#039;&#039;&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;&#039;&#039;&#039;ConsoleCommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 所以当你编写插件的时候, 确定这个指令从控制台发出后能完全正常工作是十分必要的,&lt;br /&gt;
 &lt;br /&gt;
 那些只能由在线玩家执行的指令 &#039;&#039;只能&#039;&#039; 由一个在线玩家执行。&lt;br /&gt;
 &lt;br /&gt;
 有的插件在判断命令执 行对象不是玩家时仅仅用return进行处理 (比如当有人从控制台发出指令), &lt;br /&gt;
 &lt;br /&gt;
 即便这些指令能够在控制台完美运行（比如改变天气的指令）。&lt;br /&gt;
&lt;br /&gt;
一个判断sender的方法: &lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (cmd.getName().equalsIgnoreCase(&amp;quot;basic&amp;quot;)) { //如果玩家输入了/basic则执行如下内容...&lt;br /&gt;
             // 所需要执行的事（此处略）&lt;br /&gt;
             return true;&lt;br /&gt;
         } else if (cmd.getName().equalsIgnoreCase(&amp;quot;basic2&amp;quot;)) {&lt;br /&gt;
             if (!(sender instanceof Player)) { //如果sender与Player类不匹配&lt;br /&gt;
                 sender.sendMessage(&amp;quot;This command can only be run by a player.&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 Player player = (Player) sender;&lt;br /&gt;
                    // 所需要执行的事（此处略）&lt;br /&gt;
             }&lt;br /&gt;
                 return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 在这个例子里，指令&#039;&#039;&#039;basic&#039;&#039;&#039;能由任何人发出 - 一个在线的玩家, 或者服务器后台的op. &lt;br /&gt;
 &lt;br /&gt;
 但是指令&#039;&#039;&#039;basic2&#039;&#039;&#039;只能由一个在线的玩家发出. &lt;br /&gt;
&lt;br /&gt;
 大体上说,你应该允许尽可能多的指令在控制台和玩家聊天框里都正常执行. &lt;br /&gt;
 &lt;br /&gt;
 需要在线玩家执行的指令可以使用以上例子中的机制来检测&amp;lt;code&amp;gt;&#039;&#039;&#039;CommandSender&#039;&#039;&#039;&amp;lt;/code&amp;gt;是一个玩家. &lt;br /&gt;
 &lt;br /&gt;
 很多指令广泛依靠于一些玩家的属性（例如可以被tp或被给予物品等等）。&lt;br /&gt;
&lt;br /&gt;
 如果你想更进一步, 你可以对你的指令的自变量做一些额外的检测， 例如传送指令在提供玩家ID的情况下（也只有在这种情况下）可以在控制台被执行。&lt;br /&gt;
&lt;br /&gt;
=== 使用独立的 CommandExecutor class  ===&lt;br /&gt;
&lt;br /&gt;
 以上的例子必须把 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法放在插件的主类里。&lt;br /&gt;
 &lt;br /&gt;
 对于小插件来说，这是极好的,但如果你要写一个大点的插件,把你的 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法放在独立的类里将会很有意义。&lt;br /&gt;
 &lt;br /&gt;
 幸运的是，这并不难: &lt;br /&gt;
&lt;br /&gt;
*在你的包里创建一个新类. 命名为&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;之类的 (当然，要把&#039;&#039;&#039;MyPlugin&#039;&#039;&#039;替换成你制作的插件的名字)。 这个类 &#039;&#039;&#039;必须&#039;&#039;&#039; 继承Bukkit 的&#039;&#039;&#039;CommandExecutor&#039;&#039;&#039;接口。&lt;br /&gt;
&lt;br /&gt;
*在你的插件的&amp;lt;code&amp;gt;&#039;&#039;&#039;onEnable()&#039;&#039;&#039;&amp;lt;/code&amp;gt;方法中,你需要实例化你创建的命令执行类(CommandExecutor class), 然后做一个像下面一样的调用：&amp;lt;code&amp;gt;getCommand(&amp;quot;basic&amp;quot;).setExecutor(myExecutor);&amp;lt;/code&amp;gt;&lt;br /&gt;
 其中&amp;quot;basic&amp;quot;是我们需要操控的指令, &amp;lt;code&amp;gt;myExecutor&amp;lt;/code&amp;gt; 是我们创建的实例.&lt;br /&gt;
&lt;br /&gt;
废话少说直接吔我例子: &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyPlugin.java&#039;&#039;&#039; (插件的主类): &lt;br /&gt;
 @Override&lt;br /&gt;
 public void onEnable() {&lt;br /&gt;
         // 如果你没有在plugins.yml注册过指令的话，此处会抛出空指针异常！&lt;br /&gt;
         this.getCommand(&amp;quot;basic&amp;quot;).setExecutor(new MyPluginCommandExecutor(this));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;MyPluginCommandExecutor.java&#039;&#039;&#039;（独立CommandExecutor类）: &lt;br /&gt;
 public class MyPluginCommandExecutor implements CommandExecutor {&lt;br /&gt;
         private final MyPlugin plugin;&lt;br /&gt;
 &lt;br /&gt;
         public MyPluginCommandExecutor(MyPlugin plugin) {&lt;br /&gt;
             this.plugin = plugin; // Store the plugin in situations where you need it.&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         @Override&lt;br /&gt;
         public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
             // 和以前一样执行就好…&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 请注意我们是如何把主类里的对象指向&#039;&#039;&#039;MyPluginCommandExecutor&#039;&#039;&#039;的. 这允许我们轻易的使用主类对象的方法（如果我们需要的话）。&lt;br /&gt;
&lt;br /&gt;
 这么做的话, 我们便能更好的组织代码 - 如果 &amp;lt;code&amp;gt;&#039;&#039;&#039;onCommand()&#039;&#039;&#039;&amp;lt;/code&amp;gt; 方法又大又复杂, 它就能被划分为子类，从而不使插件的主类显得凌乱。&lt;br /&gt;
&lt;br /&gt;
 PS：如果你的插件含有多个指令的话, 你需要为每一个指令单独设立一个commandexecutor.&lt;br /&gt;
&lt;br /&gt;
=== 写一个安全的指令 ===&lt;br /&gt;
 当写一个指令的时候,别进行任何假设（很重要）, 例如假设执行者一定是个玩家. 请牢记以下原则:&lt;br /&gt;
&lt;br /&gt;
==== 在命令执行前确定发出者是玩家 ====&lt;br /&gt;
&lt;br /&gt;
使用简单的例子来进行检测:&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (sender instanceof Player) {&lt;br /&gt;
             Player player = (Player) sender;&lt;br /&gt;
             // do something&lt;br /&gt;
         } else {&lt;br /&gt;
             sender.sendMessage(&amp;quot;You must be a player!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
             // do something&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 检测自变量个数 ====&lt;br /&gt;
&lt;br /&gt;
 别老是认为玩家都能打对正确数量的自变量. &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         if (args.length &amp;gt; 4) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Too many arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         } &lt;br /&gt;
         if (args.length &amp;lt; 2) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Not enough arguments!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== 在获取玩家之前先检测他们是否在线 ====&lt;br /&gt;
&lt;br /&gt;
 有时你想通过命令发出者打出的ID来获取其他玩家，请确保他们在线再说！&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
         Player target = (Bukkit.getServer().getPlayer(args[0]));&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not online!&amp;quot;);&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 如果你想对离线玩家进行操作，&amp;lt;code&amp;gt;&#039;&#039;&#039;OfflinePlayer&#039;&#039;&#039;&amp;lt;/code&amp;gt;类提供了基础操作的方法.&lt;br /&gt;
&lt;br /&gt;
== 插件的配置/设置  ==&lt;br /&gt;
Bukkit API 为插件提供了一种简便的方法，用来管理用户自定义的设置。同时，通过这种方法也可以储存数据。&lt;br /&gt;
:: &#039;&#039;详情请查看: [[Configuration API Reference]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 权限  ==&lt;br /&gt;
&lt;br /&gt;
利用新的Bukkit API来设置权限是再容易不过的事情. 检测玩家是否拥有权限，可以使用以下代码: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;if (player.hasPermission(&amp;quot;some.pointless.permission&amp;quot;)) {&lt;br /&gt;
   //执行此段代码(有权限)&lt;br /&gt;
} else {&lt;br /&gt;
   //执行此段代码(无权限)&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你也可以检测一个权限节点是否被定义过，(等效于 Java 语言中的&#039;&#039;&#039;null&#039;&#039;&#039;)，使用下面的方法: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;boolean isPermissionSet(String name)&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
你或许对此抱有疑惑:为什么这段代码中没有任何用户群的信息？因为它们并不是必需的. 之前这段代码想要实现的主要功能之一就是根据用户群格式化聊天信息. 但其实使用权限就可以很容易地完成这项工作.你可以在聊天插件的配置文件中定义权限与前缀的对应关系. 举例来说，&amp;quot;someChat.prefix.admin&amp;quot; 权限可以对应前缀[Admin].当拥有此权限的玩家发出聊天信息时 ，聊天前缀便是[Admin]. &lt;br /&gt;
&lt;br /&gt;
另一个想要实现的主要功能则是给所有拥有权限的在线用户发一条信息.你可以这样做: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;for (Player player: Bukkit.getServer().getOnlinePlayers()) {&lt;br /&gt;
    if (player.hasPermission(&amp;quot;send.receive.message&amp;quot;)) {&lt;br /&gt;
        player.sendMessage(&amp;quot;You were sent a message&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
看到这里你可能会问，若是没有权限组我该如何配置玩家的 [[权限]] ？既然 [[Bukkit]] API 本身并没有提供用户组的预设, 你必须通过安装一款权限组插件，像 [[permissionsBukkit]]  这样的插件来帮助你配置用户群. &#039;&#039;&#039;API 只是提供接口而不是提供implementation.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
=== 配置权限 ===&lt;br /&gt;
{{Hide|标题=译者注 |内容= 其一，以下代码中部分内容将以&#039;&#039;//文字&#039;&#039;翻译，使用时请不要加入其中内容，仅做教程中的翻译参考；&lt;br /&gt;
其二，default:true在下面翻译为全员默认拥有该权限，default:false则翻译为全员默认没有该权限，请注意理解。 }}&lt;br /&gt;
如果你想要更多地控制你的权限，例如设置权限的默认值或是子权限节点，不如将权限节点加入你的 &#039;&#039;[[plugin.yml]]&#039;&#039;文件.这并不是必须的，但我们推荐你这样做. 下面是一个实例，显示了当你将权限添加在&#039;&#039;plugin.yml&#039;&#039;文件末尾时的样子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;code&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands //给予使用doorman所有命令的权限&lt;br /&gt;
        children:  //子权限节点&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&lt;br /&gt;
    doorman.kick:&lt;br /&gt;
        description: Allows you to kick a user  //允许你踢出一名用户&lt;br /&gt;
        default: op  //OP默认拥有该权限&lt;br /&gt;
    doorman.ban:&lt;br /&gt;
        description: Allows you to ban a user  //允许你封禁一名用户&lt;br /&gt;
        default: op //OP默认拥有该权限&lt;br /&gt;
    doorman.knock:&lt;br /&gt;
        description: Knocks on the door! //敲门&lt;br /&gt;
        default: true //全员默认拥有该权限&lt;br /&gt;
    doorman.denied:&lt;br /&gt;
        description: Prevents this user from entering the door  //禁止拥有该权限节点的用户进入门内&amp;lt;/source&amp;gt; &amp;lt;/code&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
值得注意的是,你的插件使用的每个权限都被定义为 &#039;&#039;permissions&#039;&#039; 这个节点的子权限节点. 每个权限都能够拥有一个描述，默认值以及子权限节点。&lt;br /&gt;
==== 默认值  ====&lt;br /&gt;
&lt;br /&gt;
当一个权限没有明确是否为玩家所拥有&amp;amp;nbsp;，&#039;&#039;hasPermission&#039;&#039;&amp;lt;ref&amp;gt;这是一个检测玩家是否拥有指定权限节点的方法&amp;lt;/ref&amp;gt;&amp;amp;nbsp;将返回false&amp;lt;ref&amp;gt;该方法返回的boolean值&amp;lt;/ref&amp;gt;. 在你的[[plugin.yml]] you can change this by setting the default node to be one of four values: &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;true&#039;&#039;&#039; - 全员默认拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;false&#039;&#039;&#039; - 全员默认没有该权限. &lt;br /&gt;
*&#039;&#039;&#039;op&#039;&#039;&#039; - 如果玩家是一名OP&amp;lt;ref&amp;gt;Operator，服务器管理员&amp;lt;/ref&amp;gt;，那么他将拥有该权限. &lt;br /&gt;
*&#039;&#039;&#039;not op&#039;&#039;&#039; - 如果玩家不是一名OP，那么他将拥有该权限.&lt;br /&gt;
&lt;br /&gt;
==== 子权限  ====&lt;br /&gt;
&lt;br /&gt;
在这之前你可能已经习惯了通过权限节点自动分配所有的子权限&amp;lt;ref&amp;gt;原句:Before now you will probably be used to the * permission to automatically assign all sub permissions.此处的sub permissions应是同上文“你的插件使用的每个权限都被定义为 permissions 这个节点的子权限节点.”相照应&amp;lt;/ref&amp;gt;. 现在这种方法已经随着bukkit API&amp;lt;ref&amp;gt;应用程序编程接口(Application Programming Interface)&amp;lt;/ref&amp;gt;的更新而改变，现在你可以自己定义子权限了. 这将使得插件具有更多的灵活性. 下面是一个例子: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;yaml&amp;quot;&amp;gt;permissions:&lt;br /&gt;
    doorman.*:&lt;br /&gt;
        description: Gives access to all doorman commands  //翻译见上&lt;br /&gt;
        children:&lt;br /&gt;
            doorman.kick: true&lt;br /&gt;
            doorman.ban: true&lt;br /&gt;
            doorman.knock: true&lt;br /&gt;
            doorman.denied: false&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
在这里，&#039;&#039;doorman.*&#039;&#039; 权限拥有几个子权限并分配在其下. 子权限工作的方式就是当&#039;&#039;doorman.*&#039;&#039; 权限节点被设置为true时,子权限也会随之设置为 &#039;&#039;plugin.yml&#039;&#039;中的默认值. 然而当&#039;&#039;doorman.*&#039;&#039; 被设置为false时，所有子权限都会设置为与默认值相反的值（true-&amp;gt;false,false-&amp;gt;true）.&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 设置你自己的权限  ===&lt;br /&gt;
&lt;br /&gt;
如果你希望想知道更多有关开发你自己的权限插件的事情(即是设置权限的管理类插件) ，那么请前往查看教程&amp;amp;nbsp;[[Developing a permissions plugin]].&lt;br /&gt;
&lt;br /&gt;
== Scheduling Tasks and Background Tasks  ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 方块操控&amp;lt;br&amp;gt;  ==&lt;br /&gt;
&lt;br /&gt;
这是一个很容易的方法创建、获取或修改一个方块, 例如当你想要修改往你头上数第五格的方块时, 首先你需要做的是获取你当前的坐标点, 然后往你当前的坐标点的Y轴加上五, 获取目标方块后我们便能够很容易地修改它了, 例子如下:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@EventHandler&lt;br /&gt;
public void onPlayerMove(PlayerMoveEvent event) {&lt;br /&gt;
    // 得到玩家当前的坐标.&lt;br /&gt;
    Location loc = event.getPlayer().getLocation();&lt;br /&gt;
    // 设置目标坐标为玩家头顶 5 格处.&lt;br /&gt;
    loc.setY(loc.getY() + 5);&lt;br /&gt;
    // 得到目标坐标的方块&lt;br /&gt;
    Block b = loc.getBlock();&lt;br /&gt;
    // 将目标坐标的方块更改为石头 (stone).&lt;br /&gt;
    b.setType(Material.STONE);&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
上面的代码获取了玩家的所在位置，获取头上的第五个方块，并且放上一个石头（stone）。 Note that once you have a &amp;lt;code&amp;gt;Block&amp;lt;/code&amp;gt;, there are other things you can do besides set its type. 你可以查阅JavaDoc获取更多的东西。&lt;br /&gt;
&lt;br /&gt;
你可以用这样相似的观念去生成一些建筑，并且还可以用编程的方式通过算法生成一些独特的方块（这里指由很多个方块拼在一起的四四方方的方块）。比如，去生成一个实心的方块建筑，你可能在生成建筑的时候还需要用到&amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;循环，去一遍遍循环来生成这些建筑，填满这些建筑等。&lt;br /&gt;
下面，是一个在指定坐标生成一个实心的方块建筑的实例方法：&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void generateCube(Location loc, int length) {&lt;br /&gt;
    // 放置这个方块最中间的方块并且获取这个方块的坐标。&lt;br /&gt;
    // Uses getBlockN() instead of getN() to avoid casting to an int later.&lt;br /&gt;
    int x1 = loc.getBlockX(); &lt;br /&gt;
    int y1 = loc.getBlockY();&lt;br /&gt;
    int z1 = loc.getBlockZ();&lt;br /&gt;
&lt;br /&gt;
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.&lt;br /&gt;
    int x2 = x1 + length;&lt;br /&gt;
    int y2 = y1 + length;&lt;br /&gt;
    int z2 = z1 + length;&lt;br /&gt;
&lt;br /&gt;
    World world = loc.getWorld();&lt;br /&gt;
&lt;br /&gt;
    // Loop over the cube in the x dimension.&lt;br /&gt;
    for (int xPoint = x1; xPoint &amp;lt;= x2; xPoint++) { &lt;br /&gt;
        // Loop over the cube in the y dimension.&lt;br /&gt;
        for (int yPoint = y1; yPoint &amp;lt;= y2; yPoint++) {&lt;br /&gt;
            // Loop over the cube in the z dimension.&lt;br /&gt;
            for (int zPoint = z1; zPoint &amp;lt;= z2; zPoint++) {&lt;br /&gt;
                // Get the block that we are currently looping over.&lt;br /&gt;
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);&lt;br /&gt;
                // 设置方块ID为57号(钻石块!)&lt;br /&gt;
                currentBlock.setType(Material.DIAMOND_BLOCK); //这里下面会提到&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&amp;lt;/blockquote&amp;gt; &lt;br /&gt;
这个方法将会在程序给予这个方法高度和起始坐标（最中间的方块的坐标）时，构建一个3D的方块建筑。简单的删除方块也可以使用这个方法的思路实现，但是，但是设置方块的时候，记得把上面标注会提到的那行，设置为物品ID为0的方块(空气方块)。&amp;lt;br&amp;gt; &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== (Player) Inventory Manipulation  ==&lt;br /&gt;
&lt;br /&gt;
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&#039;s inventory&amp;amp;nbsp;:P. Here is a simple example of inventory manipulation: &lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void onPlayerJoin(PlayerJoinEvent evt) {&lt;br /&gt;
    Player player = evt.getPlayer(); // 当玩家加入游戏&lt;br /&gt;
    PlayerInventory inventory = player.getInventory(); // 获取玩家背包列表&lt;br /&gt;
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // 生成一组钻石&lt;br /&gt;
        &lt;br /&gt;
    if (inventory.contains(itemstack)) {&lt;br /&gt;
        inventory.addItem(itemstack); // 将一组钻石放到玩家的背包里&lt;br /&gt;
        player.sendMessage(&amp;quot;Welcome! You seem to be reeeally rich, so we gave you some more diamonds!&amp;quot;); //向玩家发送消息(&amp;quot;你看起来很有钱的样子....balabala&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt; &amp;lt;/blockquote&amp;gt; &lt;br /&gt;
So inside onPlayerJoin we first make a few variables to make our job easier: player, inventory and itemstack. Inventory is the player&#039;s inventory and itemstack is a ItemStack that has 64 diamonds. After that we check if the player&#039;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&#039;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!&lt;br /&gt;
&lt;br /&gt;
== Item Manipulation ==&lt;br /&gt;
&lt;br /&gt;
When dealing with items in the code, you use the ItemStack class for looking up and setting all information on that stack.&lt;br /&gt;
&lt;br /&gt;
=== Enchantments ===&lt;br /&gt;
&lt;br /&gt;
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&#039;t work) because they&#039;re abstract, so you must use an EnchantmentWrapper. If you want to enchant items that can&#039;t be enchanted inside normal SMP, use addUnsafeEnchantment() instead of addEnchantment()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
int itemCode = 280;  //use the item code you want here&lt;br /&gt;
int effectId = 20;  //use the enchantment code you want here&lt;br /&gt;
int enchantmentLevel = 100;&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(itemCode);  //new item of item code&lt;br /&gt;
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id&lt;br /&gt;
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //enchant the item&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ItemMeta ===&lt;br /&gt;
You can set the display name of an item by doing this.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
String myDisplayName = &amp;quot;Awesome Sword&amp;quot;; //这里是保存了你想要显示的名字&lt;br /&gt;
 &lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //你要更改的方块&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //得到方块的元数据&lt;br /&gt;
im.setDisplayName(myDisplayName); //设置它显示的名字&lt;br /&gt;
myItem.setItemMeta(im); //保存更改&lt;br /&gt;
 &amp;lt;/source&amp;gt;&lt;br /&gt;
You can also set the lores of an item. The lores are the small annotations on an item, like &amp;quot;+5 attack damage&amp;quot; on a stone sword.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;String&amp;gt; lores = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;
lores.add(&amp;quot;Example lore&amp;quot;, &amp;quot;this one comes on line 2&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item&lt;br /&gt;
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again&lt;br /&gt;
im.setLore(lores); //add the lores of course&lt;br /&gt;
myItem.setItemMeta(im); //give the item the new itemmeta&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maps, and Sets, and Lists, Oh My! &amp;lt;ref&amp;gt;标题中Map,Set,List均为Java类集中的接口，故不翻译&amp;lt;/ref&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
除了Map类和HashMap类，Java中还提供了其它的数据结构类集。 &lt;br /&gt;
&lt;br /&gt;
他们提供这些不同的类，是因为有些时候，使用Map类并不合适。&amp;lt;nowiki&amp;gt;[[Java data structure classes]]&amp;lt;/nowiki&amp;gt;在这个条目中，有对Java数据框架类集更深入的讲述。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;=== HashMaps概览以及如何使用此接口  ===&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;Keep in mind to never use a player in a hashmap!&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;&amp;lt;ref&amp;gt;下面有讲解。&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
应在HashMap中使用字符串.，使用 &amp;quot;p.getName()&amp;quot; 来添加，移除或是检查列表中是否含有某个玩家.&lt;br /&gt;
&lt;br /&gt;
将玩家当作object储存在HashMap中会造成大量内存泄漏.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/big&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;adam&amp;quot; as the key and assigned a value of &amp;quot;a&amp;quot; to it. That would work as intended, but then say afterwards I wanted to assign the value of &amp;quot;b&amp;quot; to key &amp;quot;adam&amp;quot; I would be able to and would get no errors but the value of &amp;quot;a&amp;quot; assigned to key &amp;quot;adam&amp;quot; in the HashMap would be overwritten because HashMaps cannot contain duplicate values. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Defining a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public Map&amp;lt;KeyType, DataType&amp;gt; HashMapName = new HashMap&amp;lt;&amp;gt;(); //Example syntax&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Example Declaration&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; pluginEnabled = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Boolean&amp;gt; isGodMode = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So inside on command you&#039;ll need this, the function name can be different but for the sake of simplicity it&#039;s best if you keep it the same. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;Player player = (Player) sender;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
togglePluginState(player);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public void togglePluginState(Player player) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Notice how we use the player name as the key here,&lt;br /&gt;
&lt;br /&gt;
    // not the player object&lt;br /&gt;
&lt;br /&gt;
    String playerName = player.getName();&lt;br /&gt;
&lt;br /&gt;
    if (pluginEnabled.containsKey(playerName)) {&lt;br /&gt;
&lt;br /&gt;
        if (pluginEnabled.get(playerName)) {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, false);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin disabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
            pluginEnabled.put(playerName, true);&lt;br /&gt;
&lt;br /&gt;
            player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    } else {&lt;br /&gt;
&lt;br /&gt;
        pluginEnabled.put(playerName, true); //If you want plugin disabled by default change this value to false.&lt;br /&gt;
&lt;br /&gt;
        player.sendMessage(&amp;quot;Plugin enabled&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== More Ideas for HashMaps  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A HashMap (or really any kind of Map in Java) is an association. It allows quick and efficient lookup of some sort of &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;value&#039;&#039;&#039;, given a unique &#039;&#039;&#039;key&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;. Anywhere this happens in your code, a Map may be your solution. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here are a few other ideas which are ideally suited to using Maps. As you will see, it doesn&#039;t have to be data that you store per player, but can be any kind of data that needs to be &amp;quot;translated&amp;quot; from one form to another. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Data Value Lookups  =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Map&amp;lt;String, Integer&amp;gt; wool_colors = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;orange&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;magenta&amp;quot;, 2);&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;light blue&amp;quot;, 3);&lt;br /&gt;
&lt;br /&gt;
   ..&lt;br /&gt;
&lt;br /&gt;
wool_colors.put(&amp;quot;black&amp;quot;, 15);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Run this in response to user commands - turn &amp;quot;green&amp;quot; into 13&lt;br /&gt;
&lt;br /&gt;
int datavalue = 0;&lt;br /&gt;
&lt;br /&gt;
if (wool_colors.containsKey(argument)) {&lt;br /&gt;
&lt;br /&gt;
    datavalue = wool_colors.get(argument);&lt;br /&gt;
&lt;br /&gt;
} else {&lt;br /&gt;
&lt;br /&gt;
    try { datavalue = Integer.parseInt(argument); }&lt;br /&gt;
&lt;br /&gt;
    catch (Exception e) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;==== Saving/Loading a HashMap  ====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you don&#039;t want an administrator to edit the data manually&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you need to save data in binary format (too complex to organize for YAML)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;you want to avoid parsing block names and/or other objects from freeform text&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is very simple way how to save any HashMap. You can replace HashMap&amp;amp;amp;lt;String, Integer&amp;amp;amp;gt; with any type of HashMap you want. Let&#039;s take an example HashMap with code to save it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        HashMap&amp;lt;String, Integer&amp;gt; mapToSave = new HashMap&amp;lt;String,Integer&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
        public void save(HashMap&amp;lt;String, Integer&amp;gt; map, String path) {&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(map);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
//Handle I/O exceptions&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
save(mapToSave, getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;);&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see it&#039;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. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public HashMap&amp;lt;String, Integer&amp;gt; load(String path) {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
Object result = ois.readObject();&lt;br /&gt;
&lt;br /&gt;
//you can feel free to cast result to HashMap&amp;lt;String, Integer&amp;gt; if you know there&#039;s that HashMap in the file&lt;br /&gt;
&lt;br /&gt;
return (HashMap&amp;lt;String, Integer&amp;gt;)result;&lt;br /&gt;
&lt;br /&gt;
} catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HashMap&amp;lt;Integer, String&amp;gt; loadedMap;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
String path = getDataFolder() + File.separator + &amp;quot;example.bin&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
File file = new File(path);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if (file.exists()) { // check if file exists before loading to avoid errors!&lt;br /&gt;
&lt;br /&gt;
loadedMap  = load(path);&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
You can use this &amp;quot;API&amp;quot; for saving/loading HashMaps, ArrayLists, and all Objects which implement Serializable or Externalizable interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;/** SLAPI = Saving/Loading API&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 * API for Saving and Loading Objects.&lt;br /&gt;
&lt;br /&gt;
 * Everyone has permission to include this code in their plugins as they wish :)&lt;br /&gt;
&lt;br /&gt;
 * @author Tomsik68&amp;lt;tomsik68@gmail.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
public class SLAPI&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; void save(T obj,String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));&lt;br /&gt;
&lt;br /&gt;
oos.writeObject(obj);&lt;br /&gt;
&lt;br /&gt;
oos.flush();&lt;br /&gt;
&lt;br /&gt;
oos.close();&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static &amp;lt;T extends Object&amp;gt; T load(String path) throws Exception&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));&lt;br /&gt;
&lt;br /&gt;
T result = (T)ois.readObject();&lt;br /&gt;
&lt;br /&gt;
ois.close();&lt;br /&gt;
&lt;br /&gt;
return result;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Example implementation of this API: &amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt;I&#039;m skipping some part of code in this source&amp;lt;nowiki&amp;gt;&#039;&#039;&#039;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;blockquote&amp;gt;&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;public class Example extends JavaPlugin {&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
private ArrayList&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onEnable() {&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
list = SLAPI.load(&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                //handle the exception&lt;br /&gt;
&lt;br /&gt;
                e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
&lt;br /&gt;
public void onDisable()	{&lt;br /&gt;
&lt;br /&gt;
            try {&lt;br /&gt;
&lt;br /&gt;
SLAPI.save(list,&amp;quot;example.bin&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            } catch(Exception e) {&lt;br /&gt;
&lt;br /&gt;
                 e.printStackTrace();&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;/blockquote&amp;gt;&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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&#039;re writing your own data object classes, and you may want to save their state using this technique, you should read about Java&#039;s Serializable or Externalizable interface. The only difference between Externalizable and Serializable is, that Serializable automatically takes all of class&#039;s fields and tries to serialize them, while Externalizable allows you to define method for reading and writing the Object. It&#039;s easy to add to your code, and it will make your data persistent with very little work required. No more parsing!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note #2: This API doesn&#039;t support changes. Once you change something in the class, data files saved with older version of your plugin won&#039;t load correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;===== Tips &amp;amp; Examples =====&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.) Simplify your save structure&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.) Save version number along with data&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always remember, that you don&#039;t know what you&#039;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&#039;t know!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.) Migrate older files&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If your plugin finds older version of some file, it should update the file accordingly and change version number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Metadata  ==&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;fields&amp;quot; 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&#039;re creating an economy plugin, you would need a HashMap of Player and Float or Double. With Metadata, you don&#039;t have to! You just attach to player new metadata value, and that&#039;s it!&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== Why to use Metadata  ===&lt;br /&gt;
&lt;br /&gt;
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps. &lt;br /&gt;
* Metadata can be used to share information between plugins.&lt;br /&gt;
&lt;br /&gt;
=== Why not use Metadata&amp;lt;br&amp;gt;  ===&lt;br /&gt;
&lt;br /&gt;
* Slightly more difficult to get the value.&lt;br /&gt;
* It is not saved on shutdown (but then again, neither are any Maps that you create).&lt;br /&gt;
&lt;br /&gt;
=== Getting &amp;amp;amp; Setting Metadata  ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {&lt;br /&gt;
  object.setMetadata(key, new FixedMetadataValue(plugin,value));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Object getMetadata(Metadatable object, String key, Plugin plugin) {&lt;br /&gt;
  List&amp;lt;MetadataValue&amp;gt; values = object.getMetadata(key);  &lt;br /&gt;
  for (MetadataValue value : values) {&lt;br /&gt;
     // Plugins are singleton objects, so using == is safe here&lt;br /&gt;
     if (value.getOwningPlugin() == plugin) {&lt;br /&gt;
        return value.value();&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
  return null;&lt;br /&gt;
}&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note: If you&#039;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.&lt;br /&gt;
&lt;br /&gt;
== Databases  ==&lt;br /&gt;
&lt;br /&gt;
Sometimes flat files aren&#039;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).&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there&#039;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!) &lt;br /&gt;
&lt;br /&gt;
=== SQLite  ===&lt;br /&gt;
&lt;br /&gt;
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&#039;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&#039;s very straightforward and shouldn&#039;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]. &lt;br /&gt;
&lt;br /&gt;
SQLite is great for very simple databases, because there&#039;s no server concerns to set up. Just make a few calls to create a new database and table. It&#039;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&#039;s often easiest and fastest to get the SQL basics squared away with SQLite, even if you &amp;quot;graduate&amp;quot; to a more server-class database engine later.&lt;br /&gt;
&lt;br /&gt;
=== MySQL  ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
== 部署你的插件  ==&lt;br /&gt;
&lt;br /&gt;
 总之你写好的你的插件,如何把它变成一个可安装在服务端的jar文件呢?&lt;br /&gt;
 &lt;br /&gt;
 首先我们需要建立一个水桶服务器。 [http://wiki.bukkit.org/Setting_up_a_server 在这里] 能找到开服相关信息.接下来你需要把插件导出为一个.jar文件，&lt;br /&gt;
 &lt;br /&gt;
 这样你就能在你的服务器里运行它。 在Eclipse里, 右键工程，点击&#039;&#039;Run as &amp;gt; Maven install&#039;&#039;即可:&lt;br /&gt;
&lt;br /&gt;
[[Image:Maveninstall.png]]&lt;br /&gt;
&lt;br /&gt;
 将来呢, 当你对插件进行了改动后,在做上述操作之前，如果你想删除之前的.jar文件，你可以右键工程，点击 &#039;&#039;Run as &amp;gt; Maven clean&#039;&#039; 。 &lt;br /&gt;
 &lt;br /&gt;
 如果你在编译插件时遇到了问题,  请检查你的JDK是否正确安装， 并浏览[[建立工作区]]条目。&lt;br /&gt;
 &lt;br /&gt;
 如果你在Eclipse控制台看到了和JDK有关的错误，你可能需要手动调整JDK,因为Eclipse的检测可能发生了错误。 &lt;br /&gt;
 &lt;br /&gt;
 点击&#039;&#039;Window -&amp;gt; Preferences&#039;&#039;, 然后是&#039;&#039;Java -&amp;gt; Installed JREs&#039;&#039;。选中你最近安装的JDK作为Java运行环境（JRE）并添加, 并取消掉原来那个报错的JDK:&lt;br /&gt;
&lt;br /&gt;
[[Image:Jrelocation.png]]&lt;br /&gt;
&lt;br /&gt;
 如果你的工程成功被编译,JAR文件会生产在 &#039;&#039;target&#039;&#039; 目录下，具体在Eclipse的工作空间里名字为工程名的文件夹中。&lt;br /&gt;
 &lt;br /&gt;
 这个JAR文件就是一个能正常工作的Bukkit插件了。当然前提是你的plugin.yml里没出差错。&lt;br /&gt;
 &lt;br /&gt;
 你可以把这个jar文件丢到服务器的plugins文件夹里,重载或重启服务器,就可以测试你的新插件啦。&lt;br /&gt;
 &lt;br /&gt;
 为了连接上使用你自己的电脑开的服务器 ,在多人游戏的ip里填上你的本地ip即可。&lt;br /&gt;
 &lt;br /&gt;
 如果出现了错误而你又不能解决的话, 试试这里[http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], &lt;br /&gt;
 &lt;br /&gt;
 在这里提问[http://wiki.bukkit.org/IRC bukkitdev IRC channel],或者重新阅读wiki。&lt;br /&gt;
 &lt;br /&gt;
 当你做完了一个实用的插件,可以考虑把它发表在这里[http://dev.bukkit.org/ dev.bukkit]（为了Bukkit社区的访问量）。&lt;br /&gt;
 &lt;br /&gt;
 以上可知,JAR格式是默认压缩生成的(JAR文件基于ZIP文件的格式)。这么说来，在上传插件到Bukkit上时把JAR文件压缩成ZIP只会增加文件体积罢了。&lt;br /&gt;
 &lt;br /&gt;
 而且呢, 放在JAR中的config文件可以在检测不到插件目录（插件自动生成的文件夹，名字与插件名相同，里面是该插件的配置）下的config文件时自动生成。&lt;br /&gt;
 &lt;br /&gt;
 所以我们一般不把jar文件转换成其他格式。&lt;br /&gt;
&lt;br /&gt;
== 导入其它插件 ==&lt;br /&gt;
你可能会希望编辑其它那些有源代码的插件. 如果插件在它的目录下有&#039;&#039;pom.xml&#039;&#039;(大多数知名插件,例如WorldEdit和Essentials都有),你可以通过点击&#039;&#039;File -&amp;gt; Import&#039;&#039;来把它作为一个工程导入, 然后打开&#039;&#039;Maven&#039;&#039;项并选择&#039;&#039;Existing Maven Projects&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
[[Image:Importmaven.png]]&lt;br /&gt;
&lt;br /&gt;
然后选择&#039;&#039;pom.xml&#039;&#039;所在的目录,这个工程就应该出现在你的侧边栏里了。就像平常一样编辑并编译它吧。&lt;br /&gt;
&lt;br /&gt;
== 小贴士  ==&lt;br /&gt;
&lt;br /&gt;
=== 使玩家着火 ===&lt;br /&gt;
&lt;br /&gt;
 Bukkit API有很多碉堡的功能。以下是实现一些功能的代码片段。 &lt;br /&gt;
&lt;br /&gt;
下面的代码运行一个玩家使另一个玩家着火。通过类似于&#039;&#039;&#039;/ignite Notch&#039;&#039;&#039;这样的指令，这个指令会让 Notch 着火。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     // 使用 equalsIgnoreCase() 来忽视大小写&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;ignite&amp;quot;)) {&lt;br /&gt;
         // 确保玩家只输入了一个自变量(要被点着的玩家名).&lt;br /&gt;
         if (args.length != 1) {&lt;br /&gt;
             // 当return false时，该指令的使用信息会显示出来。&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 确保命令发出者是个玩家.&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can set other players on fire.&amp;quot;);&lt;br /&gt;
             sender.sendMessage(&amp;quot;This is an arbitrary requirement for demonstration purposes only.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 获取那个要被点着的玩家. 请注意下标起始是0，不是1.&lt;br /&gt;
         Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
 &lt;br /&gt;
         // 确保这个玩家在线。&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // 让这个玩家着火1000 ticks (一秒钟大概有20ticks,所以总共是50秒).&lt;br /&gt;
         target.setFireTicks(1000);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 干掉玩家  ===&lt;br /&gt;
&lt;br /&gt;
 和标题一样.这里是要干掉某个玩家。 &lt;br /&gt;
&lt;br /&gt;
在你的onCommand方法里使用这个: &lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;KillPlayer&amp;quot;)) {&lt;br /&gt;
         Player target = sender.getServer().getPlayer(args[0]);&lt;br /&gt;
          // 确保玩家在线.&lt;br /&gt;
         if (target == null) {&lt;br /&gt;
             sender.sendMessage(args[0] + &amp;quot; is not currently online.&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         target.setHealth(0); &lt;br /&gt;
     }&lt;br /&gt;
     return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
这里有个小扩展,会使用一个爆炸来杀死玩家: &lt;br /&gt;
&lt;br /&gt;
 float explosionPower = 4F; //爆炸等级 - TNT的爆炸等级默认是4F&lt;br /&gt;
 Player target = sender.getWorld().getPlayer(args[0]);&lt;br /&gt;
 target.getWorld().createExplosion(target.getLocation(), explosionPower);&lt;br /&gt;
 target.setHealth(0);&lt;br /&gt;
&lt;br /&gt;
=== 创造一个假爆炸  ===&lt;br /&gt;
&lt;br /&gt;
这段代码会生产一个看起来和听起来与TNT/苦力怕产生的爆炸一样的效果。但是不会对实体和方块产生破坏。 在保留爆炸的艺术的同时又削弱了爆炸的威力，真是太有用了（&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onExplosionPrime(ExplosionPrimeEvent event) {	&lt;br /&gt;
     Entity entity = event.getEntity();&lt;br /&gt;
 &lt;br /&gt;
     // 如果事件和点燃的TNT有关,执行一些代码（又略）&lt;br /&gt;
     if (entity instanceof TNTPrimed) {&lt;br /&gt;
         entity.getWorld().createExplosion(entity.getLocation(), 0);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 使一个玩家对另外一个玩家隐形  ===&lt;br /&gt;
&lt;br /&gt;
 通过输入指令，能让指令执行者对特定的玩家隐形。 然而其他人仍然能看到指令执行者。&lt;br /&gt;
&lt;br /&gt;
 @Override&lt;br /&gt;
 public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {&lt;br /&gt;
     if (cmd.getName().equalsIgnoreCase(&amp;quot;HideMe&amp;quot;) &amp;amp;&amp;amp; args.length == 1) {&lt;br /&gt;
         if (!(sender instanceof Player)) {&lt;br /&gt;
             sender.sendMessage(&amp;quot;Only players can use this command!&amp;quot;);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         // 检测指令执行者是玩家后，我们便可以安全的进行强制转换类操作。&lt;br /&gt;
        Player s = (Player) sender;&lt;br /&gt;
 &lt;br /&gt;
        // 获取命令执行者指定的玩家&lt;br /&gt;
        Player target = Bukkit.getServer().getPlayer(args[0]);&lt;br /&gt;
        if (target == null) {&lt;br /&gt;
            sender.sendMessage(&amp;quot;Player &amp;quot; + args[0] + &amp;quot; is not online.&amp;quot;);&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        // 将 s 从 target 的视野中隐藏起来.&lt;br /&gt;
        target.hidePlayer(s);&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 在玩家准星所指处生产闪电 ===&lt;br /&gt;
&lt;br /&gt;
如下代码运行手持鱼竿的玩家通过点击来生成闪电 (当然要用准星瞄准)。 真是个简单好玩的恶作剧（。&lt;br /&gt;
&lt;br /&gt;
 @EventHandler&lt;br /&gt;
 public void onPlayerInteractBlock(PlayerInteractEvent event) {&lt;br /&gt;
     Player player = event.getPlayer();&lt;br /&gt;
     if (player.getItemInHand().getType() == Material.FISHING_ROD) {&lt;br /&gt;
         // 在给定坐标中生成一道闪电. 在本例中, 这个坐标是玩家准星瞄准的地方.&lt;br /&gt;
         // 只能指向200格以内的坐标.&lt;br /&gt;
         player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== 自动格式 ===&lt;br /&gt;
&lt;br /&gt;
 Eclipse提供了将你的代码自动转成Oracle规定的格式, 修正了不符合常规的缩进, 空格等等。 在侧边栏选中你的项目,然后选择&#039;&#039;Source -&amp;gt; Format&#039;&#039;即可对其进行更改。&lt;br /&gt;
&lt;br /&gt;
== 插件需求专区  ==&lt;br /&gt;
&lt;br /&gt;
http://forums.bukkit.org/forums/plugin-requests.13/&lt;br /&gt;
&lt;br /&gt;
== 范例文件和样板  ==&lt;br /&gt;
&lt;br /&gt;
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/wpeTPx7N Example.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]&lt;br /&gt;
&lt;br /&gt;
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have any more questions on this matter, don&#039;t hesitate to visit the BukkitDev [[IRC]] channel and ask!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:教程]]&lt;br /&gt;
[[Category:大篇幅条目]]&lt;/div&gt;</summary>
		<author><name>Tdiant</name></author>
	</entry>
</feed>