主要編輯:包括NMS/OBC下原始帖子,以顯示如何用反射做到這一點。
Player
庫存通過選擇Mojang緩存客戶端。它在打開時不需要詢問任何數據,因此它不會向服務器發送除服務器連接上的初始請求以外的任何請求。只有在進行修改時,才能測試庫存何時打開。
我不太確定爲什麼你需要這個事件,而不是一些按鍵觸發器。沒有調用InvetoryClickEvent
,廣告資源永遠不會改變,因此當玩家加入/進入他們需要跟蹤其廣告資源的狀態時,只需掃描一次廣告資源並聽取InventoryClickEvent
以處理更改。
編輯:存儲是不正確的,因爲服務器處理更改,緩存是一個更好的術語。
使用NMS/OBC:
這是可能使用反射和CraftBukkit/NMS類,但這樣做是受到經常改變,並會導致您的插件打破每次更新,除非你經得起未來考驗的反思(這個教程很豐富)。
目前存在的問題:
Bukkit不會讓你聽庫存的變化是來自給命令或直接代碼庫存manipluation。因此,我們需要添加一種方法來傾聽這些變化。要做到這一點,最好的方法是改寫清單的方法,或者添加事件以便將來進行更改,或者在那裏進行計算。我會解釋後者,因爲它更容易處理,並且還存在各種自定義事件教程。
設置環境:
當使用NMS/OBC,你需要輸入CraftBukkit得到你需要的類。下載CraftBukkit jar並將其導入到您的項目中,但您通常會這樣做。如果您收到一些方法簽名方法,請確保Bukkit具有更高的導入優先級。同樣,存在教程來向您展示如何在大多數IDE上修復該錯誤。
改寫球員盤點:
我們需要做的第一件事是創建球員清單類的子類。查看CraftPlayer
內部錯誤,我們看到它存儲了一個CraftInventoryPlayer
類,而不是PlayerInventory
,因此,這是我們必須擴展的類。創建你的新課程,延伸CraftInventoryPlayer
。確保它有一個接受net.minecraft.server.VERSION.PlayerInventory
的構造函數。版本應由當前的NMS/OBC版本字符串替換。目前爲1.7.10,版本爲v1_7_R4
。幾乎所有的Minecraft版本更改都會有所不同,並且是您的大多數版本錯誤的來源。隨着您的類擴展和構造函數調用超級構造函數,您將擁有一個基本的自定義清單。現在我們必須決定覆蓋哪些方法。
覆寫addAll
方法:
假設Essentials
和其他插件使用addAll
方法,這是我們需要監控的方法。我們要做的就是重寫addAll
方法,並將它的功能包裹在我們希望做的檢查中。在您的自定義庫存類中,我們執行以下操作。
public HashMap<Integer, ItemStack> addItem(ItemStack... items) {
HashMap<Integer, ItemStack> leftovers = super.addItem(items);
//Examination code
return leftovers;
}
此方法將調用原始addItem
方法,但可以讓你檢查返回值,並檢查什麼實際添加到播放器的庫存。通過檢查items
和leftovers
中的差異,您可以準確地確定清單中添加了哪些項目和多少項目,並在發生重要事件時調用您自己的事件和方法。因爲我不確定你要完成什麼,所以我會留下來寫你的考試代碼。
與我們的自定義類的更換球員的庫存:
我不能完全肯定在哪裏做這個替代品,但我只要我能想到的最好的選擇。我們希望在啓用插件時替換播放器的廣告資源,但是一旦它被禁用,我們需要撤消我們所做的更改,以便我們的自定義類可以正確卸載。我們將聆聽PlayerJoinEvent
和PlayerQuitEvent
做這些更改。
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
CraftPlayer craftPlayer = (CraftPlayer) event.getPlayer();
//I am not actually typing this code in an IDE, so feel free to change
//the try-catch block to only catch what is needed
try {
Field field = CraftHumanEntity.class.getDeclaredField("inventory");
field.setAccessible(true);
CraftInventoryPlayer originalInventory = (CraftInventoryPlayer) field.get(craftPlayer);
//Store inventory here, I will use a Map that would be declared above.
originalInventories.put(craftPlayer, originalInventory);
field.set(craftPlayer, new CustomInventory(craftPlayer.getHandle().inventory));
} catch (Exception e) {
Bukkit.getLogger.log(Level.SEVERE, "Error creating custom player inventory", e);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
CraftPlayer craftPlayer = (CraftPlayer) event.getPlayer();
//I am not actually typing this code in an IDE, so feel free to change
//the try-catch block to only catch what is needed
try {
Field field = CraftHumanEntity.class.getDeclaredField("inventory");
field.setAccessible(true);
CraftInventoryPlayer originalInventory = originalInventories.get(craftPlayer);
//Store inventory here, I will use a Map that would be declared above.
field.set(craftPlayer, originalInventory);
} catch (Exception e) {
Bukkit.getLogger.log(Level.SEVERE, "Error replacing player inventory with original", e);
}
}
這2位反射是複雜的,但我會盡我所能解釋它們。幾乎每個接口在CraftBukkit
中都有相應的實現,它們將接口名稱作爲「Craft」的前綴。有時單詞排序也會改變。在PlayerJoinEvent
的聽衆中,我們獲得了CraftHumanEntity
類,並獲得它的inventory
字段。這是存儲玩家庫存的字段。它是私有的,所以我們使用getDeclaredField
方法而不是getField
方法,並且必須提供聲明它的確切類。然後,我們可以訪問該字段並處理它的數據。對於玩家加入,我們get
當前CraftInventoryPlayer
存儲在該字段中,然後將其存儲在其他地方供以後檢索。然後我們set
該字段到我們的自定義清單對象。請注意,構造函數接受net.minecraft.server.PlayerInventory
,因此我們將該庫存提供給構造函數。我們終於抓住了可能發生的各種例外情況,併成功覆蓋了玩家的庫存。在PlayerQuitEvent
中,我們的做法恰恰相反,將我們的自定義廣告資源替換爲原始廣告資源,因爲我們無需再對其進行管理。
如果上面列出的任何內容都不起作用,請隨時通知我。其中大部分是理論上的,但是從相關問題的先前經驗來看,它應該起作用。
我需要這個,因爲我要調用一個函數,當一個特定的項目在播放器庫存增加。此項目由另一個插件添加。這個插件使用iventory.addItem(...)添加項目,然後不調用該事件:/。我需要一個事件,我可以使用/ give或/ item編輯Essentials提供的項目。因此,當一個項目被另一個插件添加到playerinventory中時被調用的事件。 – iComputerfreak
我已經編輯了NMS/OBC解決方案,但它會打破每一次更新,並有一些非常高的概念。 – CrypticStorm
首先感謝您的努力。 我現在做了另一種方式。當遊戲開始時,Prowalls Plugin在隨機羊毛塊上傳送我。現在我在這些羊毛塊的頂部放一塊壓力板,並抓住玩家互動事件。當玩家站在木製壓力板上並且玩家在遊戲中時,他將獲得他的物品。 PS:我現在應該將哪個問題標記爲解決我的問題的答案? – iComputerfreak