一、准备工作
配置开发环境
选择编译工具
收费软件,目前公认最好用的Java开发工具,售价颇高,但学生可以通过学生认证申请授权,授权有效期间JetBrains下所有新版本开发工具皆可免费下载使用
能够装载许多功能强大的插件,大部分免费,少部分收费
本文不提供破解方法,请自行搜索破解手段,若有能力请尽量支持正版
免费开源的JavaIDE,使用者较多。能够装载插件以扩展使用场景。
比起IDEA而言,对电脑性能的需求更低
许多方向相较IDEA皆有优有劣,区别相当大的一项是智能提示与补全功能的区别,不同人有不同的使用体验,在此不再赘述
免费软件,与其说是编译工具,倒不如说是功能更加强大的记事本,而软件的名字也恰是如此
有完整的中文化接口及支持多国语言编写的功能(UTF8技术)
功能比 Windows 中的 Notepad(记事本)强大许多,除了可以用来制作一般的纯文字说明文件,也十分适合编写计算机程序代码。不仅有语法高亮度显示,也有语法折叠功能,并且支持宏以及扩充基本功能的外挂模组
尽管软件免费开源且相当好用,但该软件作者牵扯到政治的一些行为相当离谱,请自行搜索了解后决定是否使用
除以上各软件外,还有Netbeans和MyEclipse等编译工具,但使用者较少,便不再介绍
获取Spigot服务端核心
你需要获取最新版本的BuildTools以下载自己所需版本的Spigot核心,在上方链接打开的Jenkins
页面上点击最终成功构建
字样下方的BuildTools.jar
或点击这里直接下载最新版本的BuildTools
下载成功后,在BuildTools.jar
文件放置的路径下打开cmd控制台
注1:请先把BuildTools文件转移到合适的路径下
注2:在指定文件夹下打开命令行
输入java -jar BuildTools.jar
以获取最新版本的Spigot核心
输入java -jar BuildTools.jar --rev 版本号
以获取指定版本的Spigot核心
例:java -jar BuildTools.jar --rev 1.13.2
可获取1.13.2版本
相关链接
二、新建插件
以下步骤使用编译工具为IDEA,由于加载了一些改变UI的插件,并且使用的是旧版本IDEA,因此截图中的图标、位置等信息与实际情况未必一致,请根据文字描述与实际情况来操作
新建Java项目
打开IDEA,选择Create New Project
以进入建立新Java项目的界面
若你已新建、打开,或自动启动了其他项目,则在上方工具栏中依次点选File -> New -> Project
,亦可进入同样的界面
在左侧列表选择Java
,右侧注意顶部的ProjectSDK,若此处未能读取你之前安装的JDK,可以点击New
按钮手动选择路径,除此之外不需要做其他操作,点击下方的NEXT
按钮
这页不用操作,直接NEXT
给你的项目选一个路径,再取个合适的名字,比如,SpigotDemo,然后点击FINISH
按钮
注:若你选择的路径下没有对应名字的文件夹,这一步则会弹出窗口询问你是否新建文件夹,点击确定即可
到这里,一个空的Java项目便创建好了,但在开始编写插件之前,还需要做一些准备
完善文件夹结构
右击项目根目录[SpigotDemo
],依次点击New -> Directory
,在弹出的窗口中输入lib
,点击ok
这一步新建一个文件夹以便于存放后续操作中将会用到的文件,你也可以取成其他的名字。这一步是可以跳过的,但出于各种方面的原因,我都不建议这么做。
右击src
文件夹,依次点击New -> Package
,输入main
,点击ok
右击src
文件夹,依次点击New -> Package
,输入resources
,点击ok
在这一步中新建的两个文件夹分别是存放代码主体的主文件夹和存放配置文件的资源文件夹,执行完以上新建后,文件夹构造如下图所示
仅仅是新建文件夹可还不够,你还需要将这两个文件夹标记一下
右击src
文件夹,依次点击Mark Directory as -> Unmark as Sources Root
在少数版本的IDEA里,或是根据新建项目的方式不同,有时新项目并不会将src
文件夹标为默认源文件夹,如果在Mark Directory as
中没有看到Unmark as Sources Root
,则跳过这一步
右击main
文件夹,依次点击Mark Directory as -> Sources Root
右击resources
文件夹,依次点击Mark Directory as -> Resources Root
至此,文件夹标记完毕,文件夹构造虽然没有变化,但你或许可以看到图标发生了些微改变。这里就不再截图展示了。
到这一步为止,完善文件夹结构的操作便结束了,接下来要做的则是准备必需的文件
准备文件
插件部分
右击main
文件夹,依次点击New -> Java Class
,在弹出的窗口中输入Main
,点击OK
右击resources
文件夹,依次点击New -> File
,在弹出的窗口中输入plugin.yml
,点击OK
将准备工作中下载好的Spigot核心
复制到lib
文件夹下,复制粘贴或者拖动皆可
完成上述操作后,文件排布应如下所示
依次点击上方工具栏中的File -> Project Structure
,打开Project Structure
页面
在该页面左侧选择Libraries
标签,依次点击+ -> Java
在弹出的页面里找到你Spigot核心的存放路径,若已经移至
lib
文件夹下,则直接选择lib
文件夹下的Spigot核心,点击OK。在之前若未创建
lib
文件夹,则直接从你存放的路径中寻找,注意,在这里选择完Spigot核心后,如果Spigot核心的文件存放位置发生改变,则需要重新上述步骤进行进行再设置
在弹出的窗口中选择自己的项目,点击OK
添加完成后该页面如上图所示,点击右下角的APPLY按钮保存一下上述的操作,先不急着关闭页面,直接开始下一步
点击左侧的
Artifacts
标签,依次点击+ -> jar -> Empty
在Name栏中输入一个名字,这个名字将成为你打包后的文件名
双击最右侧的'SpigotDemo' compile output
使其移到左侧
其他地方不需要修改,点击OK
至此,插件部分准备完毕,在此之外还需要准备一份可以正常运行的服务端
服务端部分
这一步其实就等同于开服,过程并不复杂,文字描述足矣,便不再附图
将Spigot核心文件拷贝到适合的空文件夹下,在该文件夹中打开命令行
输入java -jar spigot-1.16.5.jar -nogui
,回车
其中spigot-1.16.5.jar
为你的Spigot核心文件的文件名,若为其他版本核心,或被重命名成其他文件名,请根据实际情况填写
如果按照步骤来进行,初次运行是必定失败,失败时会返回如下提示
You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
翻译:"您需要同意EULA才能运行服务器。有关详细信息,请转到eula.txt。"
服务端运行时,会自动生成eula.txt
文件与server.properties
文件,你需要把eula.txt
文件中eula=false
改为eula=true
,这一操作意为"同意MINECRAFT最终用户许可协议",是启动服务端的必要项
一般而言server.properties
不需要进行修改也能启动服务端,但出于各种各样的原因(非正版玩家、网络异常无法正版登录、便于离线调试插件等),你可以把该文件中online-mode=true
改为online-mode=false
以启用离线模式
再次打开命令行,输入java -jar spigot-1.16.5.jar -nogui
,回车,等待少许时间,世界生成结束,输入help可以看到指令提示时,则说明启动已经成功了
你可以看到服务端文件夹里生成了一个plugins
文件夹,这就是你写好的插件将要放置的地方
至此,服务端部分也准备完毕
提示
1、虽然部分版本由于改动较小,可以跨几个小版本运作,但依然建议在开发插件和开服时使用同一版本的服务端核心
2、若第一次启动服务端时未能自动生成文件,请检查java的安装与配置是否完成,若已完成,则将服务端文件移出C盘或使用管理员权限打开命令行
3、你可以创建bat文件来启动服务器,而不需要每次都打开命令行,方法是新建一个.txt文件,输入java -jar spigot-1.16.5.jar -nogui
,保存,将.txt文件的后缀修改为.bat,之后便可以双击bat文件直接启动服务端了
开始编写你的插件!
一个完全空白的插件
编辑plugin.yml
文件
依照下方的格式适当修改成你自己想要的样子,或者直接复制粘贴也可以
'#'号开头的行是注释行,直接删除也不会影响这个配置文件发挥作用
#插件名称
name: SpigotDemo
#主类
main: Main
#插件版本
version: 0.0.1
#核心版本
api-version: 1.16
#作者
author: Delamer
#注册指令
commands:
#只有被注册的指令才能够正常使用
#请避免和现有指令重复
demo:
description: 这一行填写指令的介绍
usage: 这一行填写指令的用法
修改完后直接转到Main
,不用担心保存问题,IDEA会自动保存你修改的内容
新建的空白Main
文件一开始应该是这个亚子的
public class Main {
}
在Main
字样后方,花括号前方键入extends JavaPlugin
,在输入到一半的自动补全列表中选择JavaPlugin
以自动导入对应的包,或是在将该内容完整输入后,使用Alt + Enter
快捷键导入对应的包,成功导入该文件内容后如下所示
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
}
一个插件通常由主线程
,执行器
,监听器
,配置文件
等许多部件组成,其中主线程
,配置文件
是必不可少的部件,其他部件则视情况添加,现在的Main
文件继承了JavaPlugin
,则表示这个文件将是整个插件的主线程
,而plugin.yml
则是必需的配置文件
,到这里为止,一个清清白白的没有任何功能的插件就编写完毕了,试着打包一下看看
依次点击上方工具栏Build -> Build Artifacts
在弹出的窗口周选择
SpigotDemo -> Build
短暂的等待后,你的项目下应该会多出一个
out
文件夹,你的第一个插件现在就在这个文件夹里打开路径
out/artifacts/SpigotDemo
,可以看到一个SpigotDemo.jar文件把这个文件拷贝到你服务端的
plugins
文件夹下,试着运行一下服务端看看看到这条提示就意味着启动成功了,尽管没有任何功能,但这确确实实是一个可以被服务端执行的插件
接下来,让我们试着添加一些功能吧
给提示增加一点色彩
在刚在的试启动中,这个启动成功的提示太不起眼了,让我们增加一些提示,使它变得扎眼一些
在Main
中增加onLoad
,onEnable
,onDisable
方法,这些是JavaPlugin中本来就有的方法,重写这些方法的时候最好在前方加上@Override
注释。当然不加也不影响运行。
@Override
public void onLoad() {
Bukkit.getConsoleSender().sendMessage("§f<§5SpigotDemo§f>§e插件加载了");
}
@Override
public void onEnable() {
Bukkit.getConsoleSender().sendMessage("§f<§5SpigotDemo§f>§e插件启动了");
}
@Override
public void onDisable() {
Bukkit.getConsoleSender().sendMessage("§f<§5SpigotDemo§f>§e插件关闭了");
}
以上三个方法分别执行于插件加载时
,插件启动时
,插件关闭时
,其中Bukkit.getConsoleSender().sendMessage();
的效果则是在控制台输出消息。
不同于System.out.println();
,这个方法可以使用Minecraft内置的颜色代码来修改输出格式。
重新打包,覆盖plugins下的旧插件,再运行,效果如下
你可以打包运行试试效果,也可以直接进行下一步,不过
§5
这个颜色似乎有点暗,那便先做一个指令显示一下所有颜色的代码,然后换一个亮一点的代码吧。
建立执行器
在src/main
路径下新建一个类,命名为Executor
(即上文中的 [右击main
文件夹,依次点击New -> Java Class
,在弹出的窗口中输入Executor
,点击OK] 这一系列操作 )
使类实现接口CommandExecutor
,并补充必须实现的方法onCommand
,如下所示
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class Executor implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String str, String[] args) {
return true;
}
}
修改onCommand
方法内的反馈,使我们在输入指令触发该方法时,返回一串文字,比如,改成这样
public boolean onCommand(CommandSender sender, Command command, String str, String[] args) {
sender.sendMessage("§00§11§22§33§44§55§66§77§88§99§aa§bb§cc§dd§ee§ff");
return true;
}
接下来,回到Main
文件,在onEnable
方法中添加Bukkit.getPluginCommand("demo").setExecutor(new Executor());
,之后便可以在输入指令demo
时触发在执行器中编辑的方法,试试看:
执行成功。
本阶段代码:
Main.java
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
@Override
public void onLoad() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件加载了");
}
@Override
public void onEnable() {
//绑定指令与执行器
//注:此处绑定的指令必须要先在plugin.yml中注册
Bukkit.getPluginCommand("demo").setExecutor(new Executor());
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件启动了");
}
@Override
public void onDisable() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件关闭了");
}
}
Executor.java
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class Executor implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String str, String[] args) {
sender.sendMessage("§00§11§22§33§44§55§66§77§88§99§aa§bb§cc§dd§ee§ff");
return true;
}
}
建立监听器
在src/main
路径下新建一个类,命名为Monitor
使类实现接口Listener
,如下所示
import org.bukkit.event.Listener;
public class Monitor implements Listener {
}
改点什么好呢,我看这个默认的上下线提示怪难看的,就先改它吧!
原上线提示:
新上线提示:
话不多说,直接贴代码
@EventHandler
public void onPlayerJoinEvent(PlayerJoinEvent e) {
//清除原有的上线提示
e.setJoinMessage(null);
//以广播的形式发送新的上线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e上线了");
}
塞进Monitor
即可,下线提示同理
@EventHandler
public void onPlayerQuitEvent(PlayerQuitEvent e) {
//清除原有的下线提示
e.setQuitMessage(null);
//以广播的形式发送新的下线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e下线了");
}
添加完毕后,在Main
中还需要添加一行代码以注册这个编写完成的监听器
Bukkit.getPluginManager().registerEvents(new Monitor(), this);
效果:
本阶段代码:
Main.java
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
@Override
public void onLoad() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件加载了");
}
@Override
public void onEnable() {
//绑定指令与执行器
//注:此处绑定的指令必须要先在plugin.yml中注册
Bukkit.getPluginCommand("demo").setExecutor(new Executor());
//注册监听器
Bukkit.getPluginManager().registerEvents(new Monitor(), this);
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件启动了");
}
@Override
public void onDisable() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件关闭了");
}
}
Monitor.java
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class Monitor implements Listener {
@EventHandler
public void onPlayerJoinEvent(PlayerJoinEvent e) {
//清除原有的上线提示
e.setJoinMessage(null);
//以广播的形式发送新的上线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e上线了");
}
@EventHandler
public void onPlayerQuitEvent(PlayerQuitEvent e) {
//清除原有的下线提示
e.setQuitMessage(null);
//以广播的形式发送新的下线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e下线了");
}
}
现阶段结构与代码展示
结构展示:
代码展示:
Main.java
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
@Override
public void onLoad() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件加载了");
}
@Override
public void onEnable() {
//绑定指令与执行器
//注:此处绑定的指令必须要先在plugin.yml中注册
Bukkit.getPluginCommand("demo").setExecutor(new Executor());
//注册监听器
Bukkit.getPluginManager().registerEvents(new Monitor(), this);
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件启动了");
}
@Override
public void onDisable() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件关闭了");
}
}
Executor.java
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class Executor implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String str, String[] args) {
sender.sendMessage("§00§11§22§33§44§55§66§77§88§99§aa§bb§cc§dd§ee§ff");
return true;
}
}
Monitor.java
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class Monitor implements Listener {
@EventHandler
public void onPlayerJoinEvent(PlayerJoinEvent e) {
//清除原有的上线提示
e.setJoinMessage(null);
//以广播的形式发送新的上线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e上线了");
}
@EventHandler
public void onPlayerQuitEvent(PlayerQuitEvent e) {
//清除原有的下线提示
e.setQuitMessage(null);
//以广播的形式发送新的下线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e下线了");
}
}
三、附录
野路子—单文件实现全功能
或许有些人不喜欢为了寥寥两三种功能新建那么多的文件,实际上主线程内也可以同时包含执行器、监听器、补全器等等内容,只需要将Main
的代码改成下面这样的形式,则无需新建Executor
与Monitor
文件亦能实现上面的所有功能
注:该情况仅适用于功能较少的场景,否则行数过多代码难以翻阅和修改
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin implements CommandExecutor, Listener {
@Override
public void onLoad() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件加载了");
}
@Override
public void onEnable() {
//绑定指令与执行器
//注:此处绑定的指令必须要先在plugin.yml中注册
Bukkit.getPluginCommand("demo").setExecutor(this);//单文件编写插件时,此处直接指向本体
//注册监听器
Bukkit.getPluginManager().registerEvents(this, this);//单文件编写插件时,此处直接指向本体
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件启动了");
}
@Override
public void onDisable() {
Bukkit.getConsoleSender().sendMessage("§f<§aSpigotDemo§f>§e插件关闭了");
}
@Override
public boolean onCommand(CommandSender sender, Command command, String str, String[] args) {
sender.sendMessage("§00§11§22§33§44§55§66§77§88§99§aa§bb§cc§dd§ee§ff");
return true;
}
@EventHandler
public void onPlayerJoinEvent(PlayerJoinEvent e) {
//清除原有的上线提示
e.setJoinMessage(null);
//以广播的形式发送新的上线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e上线了");
}
@EventHandler
public void onPlayerQuitEvent(PlayerQuitEvent e) {
//清除原有的下线提示
e.setQuitMessage(null);
//以广播的形式发送新的下线提示
Bukkit.broadcastMessage("§e玩家§f " + e.getPlayer().getName() + " §e下线了");
}
}
善用SpigotAPI中文文档
你无法在网上找到自己所想要实现的所有效果或是相应的教程,但Spigot支持的一切方法都被列在了文档中,这篇文档的汉化程度相当高,翻阅起来相当方便。