DynamicInventoryMenu - мощная библиотека для создания динамических интерфейсов

7,116
327
1,514
Всем привет!
Эта статья про одну мою удачную разработку: небольшую библиотеку-фреймворк для баккита для создания пользовательских интерфейсов на основе инвентаря.
Приступим же к обзору!

Вопросы актуальности
Сейчас плагины, реализующие ui имеют много общего кода, который никак не связан с непосредственными задачами плагинов:
  • Обработка кликов(если вы писали ui, то помните груду if :D?)
  • Управление интерфейсами, привязанными к игрокам
  • Отображение динамически изменяемого содержимого
  • Возрастание структурной сложности
Целью разработки этой библиотеки является создание инструмента, позволяющего разрабатывать ui на бакките, не отвлекаясь на второстепенные низкоуровневые вещи, сократить вашу кодовую базу и время разработки

Чем обеспечивается динамичность?
Для начала давайте посмотрим на иерархию компонентов меню
Сами инвентари представляет экземпляры класса Menu
Слоты инвентаря представляют экземпляры MenuItem
Предметы в слотах соответствуют DataSource[ItemStack], который в свою очередь может строить их на основе некоторого Observable

Observable уведомляет о своем изменении DataSource, а DataSource уведомляет MenuItem, таким образом в слотах всегда лежат предметы, соответствующие текущему состоянию

Untitled Diagram (4).jpg
Фиолетовые стрелки показывают как в инвентаре обновляются предметы

При помощи таких базовых компонентов можно делать уже что-то более сложное, например, прокручиваемый список сделан так:
ListSource вырезает из изначальной коллекции подколлекцию Page, представляющую из себя страницу
Page может меняться при пролистывании и при изменении изначальной коллекции
Множество SelectedSource(кол-вом равным размеру страницы) выбирают из Page отдельные элементы, для которых создают ItemStack-и и передаю их MenuItem-ам
untitled-diagram-9-jpg.3974

Фиолетовые стрелки показывают как в инвентаре обновляются предметы.
Зеленые показывают как пролистывается список

Расширяя MunuItem можно делать специализированные кнопки


Давайте посмотрим, какие вещи можно уже сделать с помощь DynamicInventoryMenu:
Динамически-обновляемый список игроков, со скроллом, рассчитанный на большой онлайн

1540141942884.png

Scala:
val myAnyCollection=new mutable.HashSet[Player] with CollectionObservable[Player]

import hohserg.inventorymenu.utils.ItemUtils._

val menu1 = Menu.applyOrCreate("Players list",
  new ListView[Player](_, _, size=5*9,myAnyCollection,player=>lorize(getPlayerHead(player),player.getDisplayName),Area(1,1,7,3))
    .addScroll(0,2,DyeColor.RED,("Вверх","Страница %d/%d","Вниз"))
)

//EventHandler
//PlaerJoinEvent
myAnyCollection+=player

//PlayerLeaveEvent
myAnyCollection-=player

Че-то по-сложнее:
Список меток координат
1540148304942.png
1540149369302.png


Scala:
val marks = new mutable.ArrayBuffer[(Location, Date)] with CollectionObservable[(Location, Date)]

import hohserg.inventorymenu.utils.ItemUtils._

val df = new SimpleDateFormat("...")

val menu1 = Menu.applyOrCreate("My marks",
    new ListView[(Location, Date)](_, _, size=5*9,
        marks,
        { case (loc, date) => lorize(new ItemStack(Material.EMERALD), "" + loc.getX + " " + loc.getY + " " + loc.getZ + " " + df.format(date)) },
        Area(1, 1, 7, 3))
            .addScroll(0, 2, DyeColor.RED, ("Вверх", "Страница %d/%d", "Вниз"))
            += Button(8, 2, lorize(new ItemStack(Material.NETHER_STAR), "Добавить метку"), (player: Player) => marks += player.getLocation -> new Date())
)

На вики проекта можно найти пример более простого меню

Репозиторий проекта:
hohserg1/DynamicInventoryMenu
Вики:
DynamicInventoryMenu wiki

Подключение к вашему проекту:
Gradle:
Вместо xxx ставьте номер стабильной версии. На данный момент последняя - stable-1
Gradle (Groovy):
repositories {
    maven { url 'https://jitpack.io' }
}

dependencies {
    compile 'com.github.hohserg1:DynamicInventoryMenu:stable-xxx-SNAPSHOT'
}

Библиотека написа на скале и для скалы, однако java-саппорт будет



Статья будет дополняться
 

Вложения

  • Untitled Diagram (9).jpg
    Untitled Diagram (9).jpg
    40.7 KB · Просмотры: 277
Последнее редактирование:
Чёт ужас какой-то по коду.
Что не так с этим классом или скалкой не получается нормальный маин класс сделать? Вот вроде чел сделал.
""Вверх", "Страница %d из %d", "Вниз" " русский текст и при этом вики на английском есть. Можно же было сделать свой загрузчик языков и подгружать в зависимости от положения игрока в реальном мире(ну например получить страну где он проживает отправками NMS пакетов или с помощью протокол либы) и доп. можно добавить настройки в том же меню или в конфиг вывести настройку языка.
Стекол в твоём меню овер дохрена тем самым места практически нет и благо хоть скролл есть...
Разве так сложно сделать методы захода и входа без этого геморроя?
Kotlin:
//EventHandler
//PlaerJoinEvent
myAnyCollection+=player

//PlayerLeaveEvent
myAnyCollection-=player
"Создать коллекцию, сделать ещё чё нибудь и т.д.", хотя мог бы и сам всё отслеживать в своей библиотеки. И на сколько я знаю, при выходе игрока Player становится null, так что если надо использовать оффлайн игрока, то есть соответствующий класс, и нет необходимости добавлять или удалять игрока из коллекции. А если надо получить всех онлайн игроков, то пробегайся по getServer().getOnlinePlayers(), только в отдельном потоке пробегайся через шедулер в репиат таске.

Пока больше не смотрел ибо симпл. Вот если бы была б связка с банжи, выдача итемов и т.п. было б интересно, а так не такая уж и "мощная" библиотека.
 
Последнее редактирование:
Вау, спасибо за ревью)) Ща, посмотрю
~~~
Посмотрел
Можно же было сделать свой загрузчик языков
Это немного выходит за рамки либы. Весь текст пользователь может поставить сам. ""Вверх", "Страница %d из %d", "Вниз" " Это, например, передается через параметры создания скролла
Стекол в твоём меню овер дохрена тем самым места практически нет и благо хоть скролл есть...
Размер рабочей области определяется параметром area, ее можно растянуть на всю часть, не занятую скроллом
Разве так сложно сделать методы захода и входа без этого геморроя?
Чтобы отслживать изменение коллекции нужно смиксить к коллекции трейт. К уже существующей коллекции этого сделать нельзя. Хотя, можно рефлексией сетнуть в баккит новую с трейтом, но не факт ,что заработает, только что придумал
сам всё отслеживать в своей библиотеки
Все само и отслеживается, достаточно юзать реализацию Observable, для коллекций есть готовая
И на сколько я знаю, при выходе игрока Player становится null
Метод Bukkit#getPlayer будет возвращать null, а уже взятые экземпляры никуда не денутся
Вот если бы была б связка с банжи, выдача итемов и т.п.
Это же специфичные требования, которые выходят за рамки создания меню. Меню только отображает инфу и собирает действия игрока, все методы обработки кликов и источники данных для отображения настраивает пользователь библиотеки. Я делаю не коммон-либу для баккита
не такая уже и "мощная" библиотека
Пожалуй, мне стоит написать про Obervable и DataSource, в этих двух вещах заключается основная мощь библиотеки[/QUOTE]
~~~
Спасибо) Свой PluginPreloader я взял из ShopingCart
ScalaPlugin выглядит лучше
 
Последнее редактирование:
Назад
Сверху