哈囉大家好,這裡是吃了幾個小時錯誤的小編過路君子
外加吃了至少幾十幾百次的錯誤,慢慢嘗試才終於成功讓伺服器執行指令並且沒有任何錯誤被拋出!
小編基本上找了十幾篇的文章、論壇,基本上使用的場景都是只有單個檔案的狀況下。
換句話說,如果今天我們的專案使用上了 package 並且是要從外部的 class 去呼叫的話,那些論壇上的方式基本上不可以直接照搬,原因下面會提到。
這邊小邊就放出兩篇論壇的討論文章,基本上小編是從這兩篇慢慢地推導出來如何正確地從外部 class 去讓我們的伺服器執行指令(暱稱隨意排列),有興趣的人可以前往參考。
- fabio030, harry0198, ysl3000, escad & anikalinn. (2020, February 10). Error: Asynchronous Command Dispatch! Spigot. https://www.spigotmc.org/threads/error-asynchronous-command-dispatch.417150/
- s3ns3iw00, 24763, norska, zodiak & Drkmaster83. (2017, December 13). Dispatch Command. Spigot. https://www.spigotmc.org/threads/dispatch-command.290137/
那廢話不多說,我們開始吧,照慣例先丟上目錄結構吧:
(所有圖片點擊都可以放大、變高清)
小編這邊特別解釋 Editor.java 和 RemoteConsole.java 這兩個檔案。
Edirot.java 為插件的進入點,也就是說,onEnable() 等等事件是註冊在這個檔案內;RemoteConsole.java 則是外部 class 會去呼叫 dispatchCommand() 這個函式。
這兩個檔案都位在 package editor.passing.jinzan 下。
接下來就是一堆程式碼了,小編也不廢話了。
Editor.java
package editor.passing.jinzan; import java.lang.Runnable; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; import editor.passing.jinzan.RemoteConsole; public class Editor extends JavaPlugin implements Listener { @Override public void onEnable() { // 註冊事件觸發器 this.getServer().getPluginManager().registerEvents(this, this); // 實體化我們的虛擬控制台 // 一定要將 this 傳入,待會我們會用到 // this 的變數型態有包含 Interface org.bukkit.plugin.Plugin RemoteConsole remoteConsole = new RemoteConsole(this); } }
RemoteConsole.java
package editor.passing.jinzan; import org.bukkit.Bukkit; import java.lang.Runnable; import editor.passing.jinzan.Editor; import org.bukkit.scheduler.BukkitRunnable; class RemoteConsole { private Editor plugin; public RemoteConsole(Editor plugin) { // 初始化 this.plugin = plugin; } public dispatchCommand(String command) { // 請求伺服器將「執行我們的指令」的請求放入隊列中 new BukkitRunnable() { @Override public void run() { // 請求伺服器執行我們的指令 Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), command); } }.runTask(this.plugin); } }
Bukkit.getServer().dispatchCommand(@NotNull Plugin plugin) 必須放在 BukkitRunnable 內才不會報錯且正常執行,否則會看到以下的錯誤:
[02:05:08 FATAL]: Thread Thread-14 failed main thread check: command dispatch
java.lang.Throwable: null
at org.spigotmc.AsyncCatcher.catchOp(AsyncCatcher.java:15) ~[paper-1.18.1.jar:git-Paper-187]
at org.bukkit.craftbukkit.v1_18_R1.CraftServer.dispatchCommand(CraftServer.java:873) ~[paper-1.18.1.jar:git-Paper-187]
at editor.passing.jinzan.RemoteConsole.<init>(RemoteConsole.java:37) ~[Paper_Plugin_7-1.5.9.jar:?]
at editor.passing.jinzan.Editor$1.run(Editor.java:51) ~[Paper_Plugin_7-1.5.9.jar:?]
at java.lang.Thread.run(Thread.java:833) ~[?:?]
剩下的事情就簡單了,只要呼叫 remoteConsole.dispatchCommand() 就可以直接執行我們的指令囉。
例如 remoteConsole.dispatchCommand("say hello")。
後記
這種寫法主要是要用於複合實做上使用,就是兩個不同的套件要協同執行的時候會用,因為對方套件內絕對不會有我們需要的 API,所以我們只好自己寫一個接口傳入對方的套件內,然後直接呼叫。
當然要直接暴力一點把整個 Editor 傳進對方的套件內也不是不可以啦,但是每次要呼叫指令就要玩一次 RemoteConsole.java,跟直接呼叫 remoteConsole.dispatchCommand() 在糾錯和除錯上級別差太多了。