Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

Стартап [WIP] Code Snippets - Полезные куски кода и некоторые неочевидные вещи. [+ Ликбез]

Только, ИМХО, лучше обойтись без магических чисел и сделать что-то типа:

Java:
public static String getNmsVersion() {
    String nmsPackage = "net.minecraft.server";
    for(Package pack : Package.getPackages()) {
        String name = pack.getName();
        if(name.startsWith(nmsPackage)) {
            int versionIndex = nmsPackage.length() + 1;
            return pack.getName().substring(versionIndex);
        }
    }
}

@Dereku, приведи свой пост к какому-то окончательному варианту, чтобы я его добавил в шапку. Или так оставишь?
Согласен.Хоть тут и кода не много, но даже если так, мне что, пересчитывать количество символов в строке?А если строка длинная?А если их несколько?А если у меня переменная из конфига, мне что, каждый раз пересчитывать количество символов, залезать в проект, искать то место и менять ту самую циферку?Тут же всё сразу понятно, какая строка и что за число и в некоторых моментах поможет существенно упростить и код, и даже процесс разработки и поддержки этого кода.
 
А так почему не работает в 1.12?
Код:
public static String getNmsVersion() {
        String packageName = Bukkit.getServer().getClass().getPackage().getName();
        return packageName.substring(packageName.lastIndexOf('.') + 1);
}

Кстати "abc".length() разве подсчитывается не при компиляции?

А к теме с приоритетами событий, возможно, стоит добавить информацию о том, что в той же банже все выполняется в обратном порядке.

К порядку загрузки плагинов могу добавить про static-контент :D

Для любителей считать секунды могу посоветовать статью: https://habrahabr.ru/post/329988/
 
Последнее редактирование:
А так почему не работает в 1.12?
Код:
public static String getNmsVersion() {
        String packageName = Bukkit.getServer().getClass().getPackage().getName();
        return packageName.substring(packageName.lastIndexOf('.') + 1);
}

Кстати "abc".length() разве подсчитывается не при компиляции?

А к теме с приоритетами событий, возможно, стоит добавить информацию о том, что в той же банже все выполняется в обратном порядке.

К порядку загрузки плагинов могу добавить про static-контент :D

Для любителей считать секунды могу посоветовать статью: https://habrahabr.ru/post/329988/
Тоже работает. По идее. Но я не искал простых путей.
 
Согласен.Хоть тут и кода не много, но даже если так, мне что, пересчитывать количество символов в строке?А если строка длинная?А если их несколько?А если у меня переменная из конфига, мне что, каждый раз пересчитывать количество символов, залезать в проект, искать то место и менять ту самую циферку?Тут же всё сразу понятно, какая строка и что за число и в некоторых моментах поможет существенно упростить и код, и даже процесс разработки и поддержки этого кода.
Еще один из мухи слона делает. Видимо это заразно.
 
Определение версии nms почти без проблем или "для тех, кто столкнулся с какулей от md_5 при определении версии nms из версии сервера":

Java 8 Stream:
Java:
public static String getNmsVersion() {
    String nmsPackage = "net.minecraft.server";
    String pckg = Arrays.stream(Package.getPackages())
            .filter(pk -> pk.getName().startsWith(nmsPackage))
            .map(pk -> pk.getName()).findFirst().orElse(null);

    if (pckg == null) {
        throw new RuntimeException("Failed to recognize nms version");
    }

    return pckg.substring(nmsPackage.length() + 1);
}
Java 7:
Java:
public static String getNmsVersion() {
    String nmsPackage = "net.minecraft.server";
    for (Package pack : Package.getPackages()) {
        if (pack.getName().startsWith(nmsPackage)) {
            return pack.getName().substring(nmsPackage.length() + 1);
        }
    }
    throw new RuntimeException("Failed to recognize nms version");
}
xDark:
Java:
public static String getNmsVersion() {
    return Bukkit.getServer().getClass().getPackage().getName()
            .replace("org.bukkit.craftbukkit", "");
}
По-моему все адекватные люди третьим способом пользуются
 
Нужен гайд по фреймворку для создания комманд плагина при помощи аннотаций? Вроде кто-то просил.
@Exception_Prototype, не ты ли?
 
Ликбез: Область действия зависимостей​

Этот пост в основном относится к тем, кто пишет или когда-нибудь хочет написать свою библиотеку (не обязательно для майна), которая потом будут использовать другие люди, а не только вы сами. Впрочем, если вы не имеете никакого отношения к библиотекам, этот пост тоже может быть для вас полезен.

Всё чаще вижу, что при разработке плагинов и библиотек используют системы сборки, такие как Gradle, Maven, Ant, и это просто замечательно! Но есть одна типичная ошибка, которая напрочь убивает всю магию автоматического разрешения зависимостей, которое предоставляют эти системы сборки - все забывают про область действия зависимостей (scope в терминах Maven, configuration в Gradle).

Приведу пример

Вы модный, стильный, молодёжный и поэтому используете систему сборки для управления зависимостями. И вот вы решили подключить в свой плагин бибилиотеку. Всё, вроде, просто - указываете её в секции зависимостей и, вуаля, всё работает. Но когда дело дошло до сборки плагина в jar, вы захотели включить библиотеку в свой jar, чтобы пользователям не пришлось беспокоиться о её скачивании. И вот тут возникла проблема - плагин стал весить 2 МБ вместо 100 Кб как раньше, а всё потому что разработчик библиотеки использовал в качестве зависимости Bukkit API (не заботясь об области действия зависимости) и весь Bukkit API тоже включился в ваш плагин. Ситуация не из приятных, но самое смешное, что её могло бы просто не быть если бы разработчк изменил всего одно слово в файле сценария сборки.

Maven: compile и provided

В Maven у зависимости (dependency) есть свойство scope, которое как раз отвечает за область видимости. Всего это свойство может принимать 6 вариантов значений, но нас интересют только два: compile и provided.

Если вы не знали о существовании опции scope или знали, но ничего в ней не указали, по умолчанию будет использовано значение "compile". Область действия compile означает, что эта зависимость будет доступна всегда, то есть и во время компиляции, и во время выполнения кода, и когда вы завтракаете хлопьями с молоком. Такие зависимости являются транзитивными, это значит, что они будут подключены и к зависимым проектам. Они, обычно, включаются в исполнимый jar.
Но на самом деле такое поведение зависимостей при разработке плагинов нам нужно редко и чаще зависимость не надо включать в конечный jar, так как мы рассчитываем, что она будет предоставлена на этапе выполнения кода. Для таких зависимостей есть область действия provided. Такие зависимости не являются транзитивными и не включаются в jar.

Кратко
compile нужно использовать, когда вы подключаете api или библиотеку, которая должна являться неотъемлемой частью вашего проекта, и когда вам нужно, чтобы подключаемая зависимость была включёна в конечный jar-файл.
provide подходит для всех остальных случаев, то есть когда вы знаете, что зависимость будет предоставлена во время работы и вам не нужно включать её в jar-файл. Это именно то, что нужно когда вы подключаете в качестве зависимости Bukkit API, Spigot API, сторонний плагин и т.п.

По теме: Maven - Introduction to the Dependency Mechanism (EN)

Gradle: api, implementation и compileOnly
(если вы думаете, что можете не читать то что написано в разделе про Maven, то вы ошибаетесь :))

В Gradle вместо термина "область действия" (scope) используется термин "конфигурация" (configuration).
Конфигураций бывает очень много, но нас интересуют только три из них: api, implementation и compileOnly.

api это то же самое, что compile в Maven, а compileOnly - то же самое, что provided (название compileOnly выбрано из-за того, что такая зависимость нужна только на этапе компиляции и не должна включаться в jar).
Возникает совершенно справедливый вопрос "нафига нам знать еще про implementation?". Если объяснять хорошо и с примерами, то этот пост получится совсем уж длинным, поэтому попробую кратко. Зависимость c конфигурацией implementation как и api включается в jar, но не является транзитивной. Это сделано для того, чтобы зависимый проект не знал ничего о таких зависимостях, потому что они являются лишь частью реализации (которая может измениться), а не API. Думаю теперь стало понятно почему они называются api и implementation.

Ксати, в примерах подключения зависимостей в Gradle, которые вы найдёте в интернете, скорее всего, не будет ни api, ни implementation, ни compileOnly, а везде будет использоваться compile. Я преднамеренно про него не рассказывал потому что эта конфигурация является deprecated и вместо неё нужно использовать implementation или api.​

Кратко
implementation используется когда вам нужно, чтобы код подключаемой зависимости был включён в конечный jar-файл.
api используется, когда вы подключаете api, который должен являться неотъемлемой частью вашего проекта и может быть использован зависимыми проектами.
compileOnly подходит для всех остальных случаев, то есть когда вы знаете, что зависимость не нужно включать jar-файл. Например, Bukkit API, Spigot API, сторонний плагин и т.п.

По теме: The Java Library Plugin - Gradle User Manual (EN)
 
Последнее редактирование:
Добавлена секция "Инструменты". Добавлено три инструмента:
- Minecraft Dev: плагин для Intellij IDEA, добавляющий функционал для облегчения разработки под Minecraft, например, автогенерация EventHandler'ов, полезные рефакторинги и т.д.
- BukkitGradle: плагин для Gradle, облегчающий создание Bukkit-плагинов с использованием Gradle.
- Inspector: Bukkit-плагин, который позволяет логгировать все события Bukkit, а так же автоматически отправлять отчёты об ошибках, при их возникновении, авторам плагинов.

Добавлены ссылки:
- Работа с Bukkit API 1.13​
 
Открытие инвентаря игрока​

С этой проблемой сталкиваются все кто пытается отловить событие открытия инвентаря игрока. Событие открытия инвентаря (InventoryOpenEvent) не работает на пользовательском инвентаре. Для серверной стороны инвентарь игрока открыт всегда, когда не открыт другой инвентарь. При этом событие InventoryCloseEvent работает (хоть на том спасибо).

Если вы попробуете обхитрить систему и ловить пакеты в надежде засечь тот, который отвечает за открытие инвентаря, у вас тоже ничего не получится. Клиент просто не отправляет серверу никакой информации о том, когда игрок открывает инвентарь. Конечно, и инициировать открытие инвентаря тоже не выйдет.

Что это даёт?

Да по сути ничего хорошего. В основном это доставляет только неудобства, но всё-таки вы можете использовать тот факт, что инвентарь игрока всегда открыт. Например, вы можете положить предметы в слоты крафта (2х2) и когда игрок откроет инвентарь они будут уже лежать там. Но стоит помнить, что при выходе с сервера или при закрытии инвентаря эти предметы выпадут на землю или в инвентарь игрока. Можно пойти дальше и сделать "тихий крафт", т.к. вы не только можете класть предметы в слоты крафта, но и забирать предметы из слота результатов.

Что делать с этой странной возможностью - решать вам.
Java:
// Пример помещения предмета в слот крафта
ItemStack itemToPlace = new ItemStack(Material.DIAMOND); // Предмет, который нужно положить
player.getOpenInventory().getTopInventory().setItem(1, itemToPlace); // 0 - слот результата, 1-4 - слоты крафта
// Примечание: В слот результата не удастся ничего положить. Можно только забрать.
 
Класс, для парсинга и удобной работы с версиями в соответсвии с SemVer: https://gitlab.com/snippets/1757276

Пример использования:
Java:
Version currentVersion = Version.parseVersion("1.0.2-SNAPSHOT");

System.out.println(currentVersion.compare("1.0"));            // будет напечатано "1"
System.out.println(currentVersion.compare("1.0.2-SNAPSHOT")); // будет напечатано "0"
System.out.println(currentVersion.compare("1.0.3"));          // будет напечатано "-1"
 

Поделиться этой страницей

Назад
Верх