Событие Minecraft Bukkit API для открытия PlayerInventory

Мне нужно событие, которое вызывается при открытии PlayerInventory или изменении содержимого. InventoryOpenEvent работает только с сундуками, мебелью и т. Д., Но не с PlayerInventory. Я использую Java 1.6 SDK с API craftbukkit 1.7.

У меня на сервере Minecraft TheWalls установлен плагин ProWalls. Сейчас я пишу собственный плагин, который должен расширять этот плагин. Мой плагин просто должен позволить пользователю выбрать комплект до начала игры, а когда игра запустится и все игроки телепортируются в свои начальные места, мой плагин должен добавить элементы комплекта из комплекта, выбранного ранее.

Моя проблема теперь в том, что мне нужен момент, когда игрок телепортируется на точку возрождения. Сначала я попробовал это с помощью PlayerTeleportEvent, но пока еще рано. Предметы будут добавлены до того, как ProWalls очистит инвентарь и добавит свой предмет.

Теперь я хочу попробовать это с вызванным событием, когда предмет был добавлен в инвентарь игрока. Затем я могу проверить, является ли добавленный элемент тем, который был добавлен ProWalls в начале игры, и если это правда, я добавлю свои собственные элементы.

ProWalls вызывает метод initGameSettings () при запуске, чтобы сохранить инвентарь, очистить его и добавить свой собственный элемент. Другим возможным решением было бы создать событие, вызываемое, когда плагин ProWalls вызывает свой собственный метод initGameSettings (). Но я не знаю, возможно ли это. С моим текущим кодом я вижу предметы, добавленные из моего набора, через несколько секунд в моем инвентаре, но через миллисекунды они будут удалены. Если нет другого решения, мне, возможно, пришлось бы использовать таймер, который ждет несколько миллисекунд и добавляет элементы, чем. Но это, как я считаю, не очень хорошее решение ...

Для лучшего понимания здесь снова порядок: игрок присоединяется к TheWalls Game, плагин ProWalls вызывает initGameSettings (), плагин очищает инвентарь игрока и добавляет собственный элемент. Теперь мой плагин должен добавить элементы набора.


person iComputerfreak    schedule 25.07.2014    source источник


Ответы (2)


ОСНОВНОЕ РЕДАКТИРОВАНИЕ: включение NMS / OBC под исходным сообщением, чтобы показать, как это сделать с отражением.

Инвентарь Player кэшируется на стороне клиента по выбору Mojang. Ему не нужно запрашивать какие-либо данные при открытии, поэтому он не отправляет никаких запросов на сервер, кроме начального запроса при подключении к серверу. Невозможно проверить, когда инвентарь открыт, только когда внесены изменения.

Я не совсем уверен, зачем вам это событие, кроме как для какого-то триггера нажатия клавиш. Инвентарь никогда не изменится без вызова InvetoryClickEvent, поэтому, когда игрок присоединяется / входит в состояние, в котором ему необходимо отслеживать его инвентарь, просто просканируйте его инвентарь один раз и прислушайтесь к InventoryClickEvent, чтобы обработать изменения.

РЕДАКТИРОВАТЬ: Сохранено неверно, поскольку сервер обрабатывает изменения, кэширование - лучший термин.

Использование NMS / OBC:

Это возможно с использованием отражения и классов CraftBukkit / NMS, но это может часто меняться и приведет к тому, что ваш плагин будет ломать каждое обновление, если вы не защитите свое отражение в будущем (учебников для этого предостаточно).

Текущая проблема:

Bukkit не позволяет вам отслеживать изменения инвентаря, которые происходят из-за команд отдать или прямого манипулирования инвентаризацией кода. Таким образом, нам нужно добавить способ прослушивания этих изменений. Лучший способ сделать это - перезаписать методы, которые изменяют инвентарь, и либо добавить события для облегчения будущих изменений, либо провести там вычисления. Я объясню последнее, так как с ним проще иметь дело, а также существуют различные учебные пособия по настраиваемым событиям.

Настройка среды:

При использовании NMS / OBC вам необходимо импортировать CraftBukkit, чтобы получить нужные вам классы. Загрузите банку CraftBukkit и импортируйте ее в свой проект, как обычно. Если вы получаете некоторые методы подписи методов, убедитесь, что 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 фрагмента отражения довольно сложны, но я объясню их как можно лучше. Почти каждый Bukkit интерфейс имеет соответствующую реализацию в CraftBukkit, в которой к имени интерфейса добавляется префикс «Craft». Иногда меняется и порядок слов. В слушателе PlayerJoinEvent мы получаем класс CraftHumanEntity и поле inventory. Это поле, в котором хранится инвентарь игрока. Он является частным, поэтому мы используем метод getDeclaredField, а не метод getField, и должны предоставить точный класс, который его объявляет. Затем мы делаем поле доступным и манипулируем его данными. Для присоединения игроков мы get текущее CraftInventoryPlayer, которое хранится в этом поле, а затем сохраняем его в другом месте для последующего извлечения. Затем мы set это поле в нашем настраиваемом объекте инвентаризации. Обратите внимание, что конструктор принял net.minecraft.server.PlayerInventory, поэтому мы предоставляем этот инвентарь конструктору. Мы наконец-то перехватили все возможные исключения, которые могли здесь произойти, и успешно перезаписали инвентарь игрока. В PlayerQuitEvent мы делаем наоборот, заменяя наш собственный инвентарь оригинальным, потому что нам больше не нужно им управлять.

Если что-то из перечисленных выше не работает, дайте мне знать. По большей части это теоретически, но, судя по предыдущему опыту решения подобных проблем, это должно сработать.

person CrypticStorm    schedule 25.07.2014
comment
Мне это нужно, потому что я хочу вызвать функцию при добавлении определенного предмета в инвентарь игрока. Этот элемент добавлен другим плагином. Этот плагин добавляет элемент с помощью iventory.addItem (...), а затем событие не вызывается: /. Мне нужно событие, с которым я могу, например, отредактировать элемент, предоставленный Essentials, с помощью / give или / item. Итак, событие, которое вызывается, когда элемент добавляется в инвентарь плеера другим плагином. - person iComputerfreak; 25.07.2014
comment
Я редактировал решение NMS / OBC, но оно нарушает каждое обновление и содержит некоторые очень высокоуровневые концепции. - person CrypticStorm; 28.07.2014
comment
Во-первых, спасибо за ваши усилия. Теперь я сделал это по-другому. Плагин Prowalls телепортирует меня на случайный блок шерсти при запуске игры. Теперь я помещаю нажимную пластину поверх этих шерстяных блоков и ловлю событие PlayerInteraction. Когда игрок стоит на деревянной нажимной пластине и находится в игре, он получает свои предметы. PS: Какой вопрос я должен сейчас отметить как ответ, решивший мою проблему? - person iComputerfreak; 31.07.2014
comment
Вы сами придумали обходной путь, но я считаю, что я нашел ответ на исходную проблему. Если вы тоже в это верите, отметьте это как таковое, иначе не делайте этого. Теперь, когда вы не можете отмечать комментарии как ответ. - person CrypticStorm; 31.07.2014

Вы можете использовать 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;

Тогда у вас есть свое мероприятие!

(Я не тестировал, но у меня сработал похожий код ...)

person writzlpfrimpft    schedule 02.12.2016