2014-07-25 90 views
0

當打開PlayerInventory或更改內容時,我需要一個被調用的事件。 InventoryOpenEvent僅適用於箱子,傢俱等,但不適用於PlayerInventory。 我正在使用Java 1.6 SDK和craftbukkit 1.7 API。Minecraft Bukkit API Event for opening PlayerInventory

我在我的Minecraft TheWalls服務器上安裝了插件ProWalls。現在我正在編寫一個自己的插件,它應該擴展這個擴展。我的插件應該讓用戶在遊戲開始之前選擇套件,當遊戲開始並且所有玩家被傳送到他們的開始位置時,我的插件應該添加之前選擇的套件的套件項目。

我的問題是現在,我需要的時刻,當玩家被傳送到出生點。 我第一次使用PlayerTeleportEvent來試用它,但這還爲時過早。在ProWalls清除庫存並添加自己的物品之前,物品將被添加。

現在我想嘗試一個事件調用,當一個項目被添加到播放器的庫存。然後我可以檢查添加的項目是否是ProWalls在遊戲開始時添加的項目,如果這是真的,我會添加自己的項目。

ProWalls在開始時調用initGameSettings()方法來保存庫存,清除它並添加他自己的物品。因此,另一個可能的解決方案是,在插件ProWalls調用他自己的方法initGameSettings()時創建一個事件。但我不知道,如果這是可能的。 使用我當前的代碼,我可以在清單中看到從我的套件中添加的項目,但毫秒後,這些項目將被刪除。如果沒有其他解決方案,我可能需要一個計時器,等待幾毫秒並添加項目。但這是,因爲我覺得,沒有什麼好辦法......

爲了更好地理解這裏的順序重新: 玩家加入遊戲TheWalls,ProWalls插件調用initGameSettings(),插件清除玩家的庫存,並增加了自己的項目,現在我的插件應該添加套件的項目。

回答

1

主要編輯:包括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方法,但可以讓你檢查返回值,並檢查什麼實際添加到播放器的庫存。通過檢查itemsleftovers中的差異,您可以準確地確定清單中添加了哪些項目和多少項目,並在發生重要事件時調用您自己的事件和方法。因爲我不確定你要完成什麼,所以我會留下來寫你的考試代碼。

與我們的自定義類的更換球員的庫存:

我不能完全肯定在哪裏做這個替代品,但我只要我能想到的最好的選擇。我們希望在啓用插件時替換播放器的廣告資源,但是一旦它被禁用,我們需要撤消我們所做的更改,以便我們的自定義類可以正確卸載。我們將聆聽PlayerJoinEventPlayerQuitEvent做這些更改。

@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中,我們的做法恰恰相反,將我們的自定義廣告資源替換爲原始廣告資源,因爲我們無需再對其進行管理。

如果上面列出的任何內容都不起作用,請隨時通知我。其中大部分是理論上的,但是從相關問題的先前經驗來看,它應該起作用。

+0

我需要這個,因爲我要調用一個函數,當一個特定的項目在播放器庫存增加。此項目由另一個插件添加。這個插件使用iventory.addItem(...)添加項目,然後不調用該事件:/。我需要一個事件,我可以使用/ give或/ item編輯Essentials提供的項目。因此,當一個項目被另一個插件添加到playerinventory中時被調用的事件。 – iComputerfreak

+0

我已經編輯了NMS/OBC解決方案,但它會打破每一次更新,並有一些非常高的概念。 – CrypticStorm

+0

首先感謝您的努力。 我現在做了另一種方式。當遊戲開始時,Prowalls Plugin在隨機羊毛塊上傳送我。現在我在這些羊毛塊的頂部放一塊壓力板,並抓住玩家互動事件。當玩家站在木製壓力板上並且玩家在遊戲中時,他將獲得他的物品。 PS:我現在應該將哪個問題標記爲解決我的問題的答案? – iComputerfreak

0

可以使用InventoryOpenEvent:

@EventHandler 
public void onInvOpen(InventoryOpenEvent e) 

也許你得檢查球員:

if(!(e.getPlayer().equals(anotherplayer))) 
    return; 

然後你就可以檢查庫存玩家的盤點:

if(e.getInventory() !instanceof PlayerInventory 
    return; 

(這只是檢查庫存是否爲一個玩家的庫存)

您還可以檢查其庫存的球員是通過檢查庫存持有者:

Player invholder = (Player) e.getInventory().getHolder(); 
if(!invholder.equals(*anotherplayer*)) 
    return; 

然後你有你的活動!

(我沒有測試它,但類似的代碼爲我工作...)

相關問題