ОСНОВНОЕ РЕДАКТИРОВАНИЕ: включение 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