Результаты поиска
-
Эклипсом по баккиту
Дабы не изобретать велосипед, дам лучше ссылку на прекрасную статью Пишем простой плагин для Bukkit за авторством DmitriyMX, в которой он простыми русскими буквами описал в том числе и процесс установки и первоначальной настройки Эклипса. В свое время осваивал сей процесс именно по этой статье, не вижу причин, почему бы вам не сделать то же самое. К сожалению, некоторые моменты (и ссылки) в статье уже устарели, но в общем и целом она пока является вполне годным материалом для новичков.
Тезисно изложу некоторые основные моменты:
1. Эклипс качается в виде инсталлятора (для пользователей Windows, предпочтительно 64-битный - если позволяет система). Запускается, в открывшемся списке выбирается первый пункт (что-то вроде "Eclipse IDE for Java developers"). Далее выбирается версия среды (Eclipse - "затмение", поэтому названия разных версий посвящаются тому или иному объекту на небесной тверди), указываются путь установки, путь к установленному JRE. После этого производится непосредственно установка среды.
2. При первом запуске Эклипса вас попросят определиться с т.н. рабочим пространством (Workspace), то есть, указать каталог, где в дальнейшем будут размещаться ваши проекты. Надеюсь, вы достаточно опытны, чтобы не кидать все это на рабочем столе или в каталоге с установленным Эклипсом? Выделите под это дело отдельный каталог на диске D, например, d:\Projects\ - и искать будет проще, и меньше риска, что в один прекрасный солнечный день вы начнете биться головой об угол двери, когда ваша винда (да, вышесказанное относится в основном к счастливым обладателям продукта от дяди Билли) прикажет жить долго и счастливо.
3. Желательно в настройках среды (Window->Preferences) включить нумерацию строк в текстах исходных файлах (раздел General->Editor->Text Editors, галочка "Show line numbers"), а также в разделе General->Workspace установить кодировку по-умолчанию (в "Text file encoding" выбрать "UTF-8") - это позволит вам в дальнейшем избежать проблем с русскими символами при запуске ваших плагинов. Я обычно еще в разделе General->Editors->File associations добавляю расширения "*.properties", "*.yml" и "*.json", которым в качестве дефолтного текстового редактора назначаю Notepad++.
Теперь пару слов о том, куда будет складываться конечный результат, то есть откомпилированный jar-файл плагина. Падать он должен в папку plugins/ вашего сервера. А какой версии должен быть сервер, спросите вы? В идеале - самой новой (1.8.8 на момент написания статьи). Однако на деле это часто зависит от того, какая версия используется на вашем рабочем сервере.
А сколько вообще нужно серверов? Тут тоже одной фразой не ответишь. Если вы пишете плагины исключительно для собственного сервера, то их должно быть как минимум два: один, назовем его условно "рабочий" (work) - это сервер, на котором, собственно, и тусуется народ. Он неприкосновенен, плагины на нем обновляются только тогда, когда есть полная уверенность, что они будут работать на нем бэз шуму и пыли. Причем делать это желательно через остановку сервера (в крайнем случае - через его перезапуск сразу после замены файлов). Второй, назовем его "подопытный" (dev) - на нем вы будете тренироваться, как на кошечках, именно в его папку plugins/ и следует сваливать скомпилированные jar-файлы ваших плагинов. При этом перезапуск плагинов на нем производится, как правило, командой reload с консоли сервера (либо /reload из клиента). Хоть баккит в последних версиях настоятельно не рекомендует использовать эту команду, но от ее поддержки не отказался. Да, при подобном способе перезапуска возможны ошибки (и рано или поздно они случаются), но в 99% релоадов этот процесс проходит безболезненно.
Очень желательно, чтобы серверы эти располагались на разных машинах, дабы эксперименты с dev-сервером не привели к подвисанию рабочего. Конечно, запускаются они в разных "песочницах" ява-машин, однако и без этого есть множество способов сделать каку: от конфликтов при доступе к орбщим файлам до манипуляций с общими базами данных. Да и настраивать серверы при наличии двух машин будет проще - можно будет полностью скопировать каталог рабочего сервера на свой компьютер, сохранив порт доступа и получив идентичный набор плагинов и используя копию рабочей карты (которую в дальнейшем можно будет просто обновлять с рабочего сервера).
В особых случаях, когда рабочий сервер является серьезным объектом, как например наш Мinecrafting.ru, в пару к нему на той же машине поднимается тестовый сервер (с другим рабочим портом и вайт-листом). Он может либо работать в постоянном режиме, как рабочий, либо запускаться по требованию. С некоторой периодичностью на него заливается бэкап рабочей карты. Используется он для финишной шлифовки плагинов, отработки пермишенсов (система прав на нем копируется, как правило, с рабочего сервера). На нем же производится строительство объектов для рабочей карты, которые затем в виде схематиков копируются на рабочий сервер.
В общем и целом, ваш dev-сервер должен располагаться в легкодоступном месте, например, d:\bukkit_1.8\. Указывать версию для каталога сервера очень желательно, ведь у вас еще может быть сервер для более старой версии, например, d:\bukkit_1.7\, на котором вы будете тестировать совместимость вашего плагина. Просто менять jar-файл сервера разных мажорных версий в одном и том же каталоге не получится, поскольку структура каталогов и файлов в разных версиях различается довольно серьезно.
И еще, прежде чем перейти непосредственно к описанию того, что из себя представляет плагин, хочется сделать несколько замечаний на тему того, как избежать стандартных ошибок, на исправление которых я потратил ОЧЕНЬ много времени и сил:
1. Не пихайте все в одну кучу. Если ваш плагин выполняет какую-то простейшую задачу (например, выдает игроку сообщение при входе на сервер), это еще прокатит, однако в дальнейшем ни к чему, кроме путаницы, не приведет. Образно это можно представить как 103: десять строк -> десять методов -> десять классов. То есть, методы ваших классов должны содержать не более чем по 10 строк, в классе не должно быть больше десятка методов, а пакет должен состоять не более чем из 10 классов. Конечно же, это недостижимый идеал. Не нужно упираться рогом для того, чтобы подогнать под него ваш проект. Однако ненавязчивое стремление к нему очень хорошо дисциплинирует, значительно повышает читабельность текста и в итоге позволяет значительно отодвинуть тот неизбежный момент, когда проект без коренной модернизации попросту начинает "сыпаться". Если есть возможность вынести некий функционал в отдельный метод, а то и в отдельный класс - это надо сделать. Это позволит вам в дальнейшем просто вставлять вызов необходимого метода, а не заниматься копипастом. Опять же, если в одном пакете скопилось несколько десятков файлов - это уже повод задуматься о создании подпакетов и вынесении туда определенных классов, группируя их по схожести функций.2. Давайте переменным (и уж тем более методам, классам и пакетам) осмысленные названия. Я еще допускаю варианты i, j, k в качестве названий переменных цикла - это вполне себе устоявшаяся практика. Но все остальное должно иметь название, отражающее их смысл: buffer, UserSystem, ru.vasco.UserSystem. Как только вы опуститесь до употребления чего-то вроде var12, Class1, Class2 - ждите проблем. Также желательно, чтобы названия элементов были как можно более полными (читай, длинными). Как ни странно, это, как правило, ускоряет доведение кода до рабочего состояния. Какую конкретно стилистическую систему применять для именования объектов - дело ваше, но система эта должна быть.
3. Забудьте про использование операции конкатенации для склейки строк:
String str= "Пользователь " + player.getName() + " вошел на сервер";
Одна строка, конечно, погоды не сделает, но вот сотни строк, ежесекундно сливающиеся в объектно-ориентированном экстазе, рано или поздно скушают память вашего сервера, сколько бы вы ее не выделили. Причем проблема даже не в скушанной памяти как таковой, а во времени, которое сборщик мусора (Garbage collector) виртуальной машины (JVM) потратит на ее освобождение. Проблема эта (конкатенации строк) связана с особенностями реализации строк в языке Java и лечится следующим способом:
StringBuilder str= new StringBuilder("Пользователь ").append(player.getName()).append(" вошел на сервер"); ... ... str.ToString() ... // преобразование объекта StringBuilder в строку
Да, получается немного длиннее, но, поверьте, ваш сервер вам будет весьма благодарен.
4. Комментируйте сложные куски кода. Вставить пару слов перед телом какого-нибудь цикла (особенно вложенного) несложно, а вернувшись к этому куску кода через несколько месяцев, вы уже не будете смотреть на него как баран на новые ворота. В обязательном порядке необходимо добавлять комментарии к объявлениям методов и классов.
5. Не используйте без необходимости конструкции try...catch как средство "исправления" ошибок времени выполнения программы. Вернее, используйте их только тех местах, где их использование совершенно необходимо. Например, при вызове методов пакета lang.reflect или при работе с запросами к базам данных. По-первости очень соблазняет возможность закрыть все проблемы конструкцией:
try { ... } catch (Exception e) {}
Казалось бы, нет ошибки - нет проблемы. Однако на самом деле ошибка никуда не делась, она просто не отображается в логе и в консоли. Только узнаете вы об этом какое-то время спустя и только по косвенным результатам - в основном по неправильной работе алгоритма обработки данных. И лечить эти ошибки, поверьте, будет гораздо сложнее. Так что учитывайте парадоксальную вещь: если в консоль сыпятся исключения - это хорошо, значит можно посмотреть - что это за исключения, найти метод, который их генерит и быстро устранить причину этого. В дополнение к этому небольшое замечание: если при вынужденном использовании конструкции try...catch есть возможность как-то конкретизировать ситуацию - это, опять же, надо сделать. Например, при работе с базами данных следует использовать конструкции вида:
try { ... } catch (SQLException e) { ... }
Это позволит перехватывать только исключения sql-запросов, пропуская все остальное дальше. Причем даже в этом случае очень желательно не "гасить" ошибки, а как-то реагировать на них, например, сообщением в консоль сервера.
6. Основная часть кода программы, плотно взаимодействующей с пользователем - это т.н. "защита от дурака". Поэтому код вашего плагина не менее чем на 75% должен состоять из конструкций if(). Не ленитесь проверять и перепроверять все значения, поступающие на вход. Это убережет вас от постоянных эксцепшенов в консоли а то и от крашей сервера. Желательно все проверки вынести на самую раннюю стадию: во-первых, это разгрузит ваш основной код от лишнего обвеса, как-бы разделив его на две части - блок взаимодействия с пользователем и основной целевой блок, а во-вторых, лишние проверки, вынесенные наверх из тела рабочего цикла (а то и неоднократно вложенных циклов) позволят сэкономить пару-тройку
шекелеймилисекунд/мегабайт из выделенных плагину ресурсов сервера.7. При использовании т.н. generic-классов, например, ArrayList или HashMap не ленитесь указывать, чем параметризируется данный класс:
Map<String, Player> players= new HashMap<String, Player>(); List<ItemStack> items= new ArrayList<ItemStack>();
Помимо того, что вам не придется отключать в IDE-среде соответствующее предупреждение, вы еще получите дополнительную страховку от неочевидных ошибок преобразования типов. Да, иногда бывают ситуации, когда списки состоят из объектов разных типов. Но даже в этом случае чаще всего можно свести их к одному общему супертипу и указать его как параметризирующий для дженерик-класса.
8. Заведите в корне вашего проекта файлик todo.txt и записывайте туда построчно все, что вам хотелось бы (или вас попросили) допилить в вашем плагине. Если этого не делать, через две недели вы благополучно об этом забудете. Замечания и пожелания можно предварять знаком '-', после реализации задуманного можно менять его на '+'.
Вот теперь, наверное, можно уже поговорить о том, из чего состоит плагин и как он работает.- 15.12.2015 23:20
- by vasco
-
Ведро с гайками
Основным достижением данной модификации сервера майна является серьезная переработка серверного цикла: была проведена серьезная работа по оптимизации обработки чанков, сущностей, обмена с клиентской частью. Переработан компонент спавна мобов, в бакките тысячи мобов уже не являются проблемой для сервера. Также под это дело появились дополнительные настройки в собственном конфигурационном файле (bukkit.yml). Однако с точки зрения разработчика плагинов главная заслуга баккитовцев не в этом. Главное, что они сделали - это сконструировали великолепный механизм взаимодействия (Bukkit API) между плагинами и сервером. Хотя, надо сказать, сделали они это несколько странным способом: создали в пакете org.bukkit дополнительную иерархическую структуру с интерфейсами, которые полностью повторяют своими названиями классы в пакете org.bukkit.craftbukkit.v1_X_RY, только не имеют в названии префикс <Craft>. То есть, класс CraftEntity из пакета org.bukkit.craftbukkit.v1_X_RY.Entity наследует интерфейс Entity из пакета org.bukkit.Entity, причем все события и методы работают именно с объектами Entity, по сути, с интерфейсами. Умом я понимаю, что это связано с ограниченностью явы в плане множественного наследования классов, но в душе воротит от такой двойной бессмысленной работы.
Что еще ощутимо напрягает, так это методы, которыми действуют (а может - уже "действовали") "баккитовцы": баккит не является в чистом виде надстройкой над NMS, как, казалось бы, должно быть при строгом следовании принципам ООП, он с NMS образует чудную мешанину из кода, вызовы идут не только сверху вниз, как обычно происходит, в коде классов NMS не менее часто встречаются вызовы "наверх", к методам баккита. Особенно часто это происходит в ключевых точках генерации событий (баккитовских). В принципе, логика этого явления понятна: если у исходного продукта (NMS) нет достаточного количества контрольных точек (событий), к которым можно "прицепиться" для обработки тех или иных ситуаций, нет другого пути, кроме как вручную формировать такие точки, меняя напрямую его код. Однако методы, с помощью которых это было достигнуто, вызывают иногда по меньшей мере удивление. Хотя, нам тут просто сидеть и рассуждать - как оно на самом деле надо было сделать, и как бы мы на самом деле сделали все гораздо лучше (быстрее, дешевле - нужное подчеркнуть).
Собственно, вот эта самая совокупность интерфейсов из пакета org.bukkit, уже, как видно из названия, изолированная от версии NMS, и соответствующий им набор классов org.bukkit.craftbukkit.v1_X_RY, все еще привязанный к конкретному NMS, и называются Bukkit, он же баккит, он же ведро - прошу любить и жаловать. По сути, его же можно называть и CraftBukkit, что иногда и делают, хотя идеологически это все же немного разные вещи: CraftBukkit является модернизацией оригинального майнкрафт-сервера, а Bukkit является программным интерфейсом (API), связующим звеном между сервером и плагинами. Однако все уже давно привыкли рассматривать это как единое целое и привычно используют термин Bukkit.
Вообще, главное богатство баккита заключается не в том, что он завернул все оригинальные классы в красивую обертку, а в том, что у него имеется огромное количество разного типа событий (несколько десятков), генерируемых сервером (теперь уже под управлением баккита) ежесекундно, и сообщающих о возникновении разного рода ситуаций. Теперь любой плагин может зарегистрировать на сервере свой обработчик событий нужного типа и выполнять какие-нибудь действия по возникновению оного. Это покрывает 99% всех потребностей плагинописателей, есть множество вполне серьезных плагинов, которые даже не лезут глубже баккитовских интерфейсов и событий.
Зачем же люди морочатся с низкоуровневым доступом к объектам NMS, рискуя заработать несовместимость своего плагина с будущими версиями сервера? Дело в том, что даже баккит, при все богатстве его средств, не всегда может обеспечить полный спектр, так сказать, услуг и сервиса. Например, в нем до сих пор нет нормального интерфейса для работы со сделками торговца-непися, нет возможности отправить клиенту кастомный пакет, многие характеристики сущностей также доступны только средствами NMS. Поэтому волей-неволей периодически приходится лезть на уровень NMS и добывать информацию оттуда. Делается это, как уже было сказано выше, через метод getHandle():void process(Entity entity)
{
CraftEntity ce= (CraftEntity)entity;
net.minecraft.server.v1_8_R3.Entity handle= ce.getHandle();
if (handle instanceof net.minecraft.server.v1_8_R3.Entity)
{
...
}
}
То есть, сначала объект приводится к классу CraftBukkitа, соответствующему данному интерфейсу, а уж затем для полученного объекта пытаемся получить соответствующий ему NMS-объект. Желательно при этом проверять полученный результат на "вшивость", дабы не получить исключение со всеми вытекающими. Можно, в принципе, опустить эту проверку, поскольку баккитовский объект в отрыве от соответствующего ему NMS-объекта - это нонсенс. Но береженого, как говорится, бог бережет. Тем более, что сам баккит именно так и делает внутри методов своих классов. Отдельно нужно отметить, что иногда (в редких случаях) нет прямого соответствия классов баккита и NMS и, соответственно, у такого класса нет метода getHandle(), либо он может называться иначе. Исходники вам в помощь, бесценный кладезь информации по программированию на яве вообще и по устройству майнкрафтовского сервера в частности.
Самое интересное, что на этом дело не закончилось. Компания еще более ушлых товарищей решила, что баккит уже не торт, и запилила свою сборку на его основе, назвав ее Spigot. С потоками и таймингами. Функционально они добавили всего-ничего, но вот оптимизировали они его еще более серьезно, добавив по сложившейся традиции в систему еще один конфиг-файл spigot.yml, в котором также имеется куча настроек по дальнейшему тюнингу системы. Особенно хорошо преимущества спигота раскрываются на многопроцессорных системах и с хорошим онлайном.
А потом, в один прекрасный день, баккит как-бы приказал жить долго и счастливо. По крайней мере, народ высказал свое твердое и категоричное "фи" на известную тему (см. Страсти по ведру, еще и еще). А потом, после слива толстого небритого мужика в дурацкой шляпе и продажей всего нажитого жадному Билли дела и вовсе пошли по известному маршруту. В итоге компания негодующих активистов объединилась для создания мегамодификации мейнкрафт-сервера под названием Sponge (губка) на основе не менее легендарной системы тюнинга клиента майнкрафта Forge. Обещают задвинуть и баккит и спигот (тем более, что спиготовцы, вроде как, тоже решили поучаствовать в гешефте). Что они там наваяли еще не видел, но боюсь даже представить, во что выльется переделка всех написанных плагинов под новый API. Но, похоже, рано или поздно сделать это все равно придется.
Кстати, об исходниках. Поскольку исходных кодов ни NMS ни баккита в свободном доступе нет и не предвидится, приходится как-то выкручиваться. Для выкручивания используется обычно маленькая, но гордая весьма полезная утилита FernFlowe. Я использую ее с небольшим патчиком, который исправляет кое-какие огрехи декомпиляции сложных классов. Пропатченный FernFlower можно скачать с нашего сайта. Утилита консольная, поэтому для декомпиляции jar-файлов использую обычно командный файл такого вида:java -jar fernflower.jar %1 decomp\ pause
Как видно из примера, в каталоге с утилитой необходимо создать подкаталог decomp/. После этого перетаскиваем мышью (в винде, конечно) jar-файл, который нуждается в декомпиляции, на командный файл, в результате чего тот стартует и запускает утилиту fernflower.jar, передавая ему в качестве параметра декомпилируемый файл. Утилита шуршит некоторое время, захламляя черный экран белыми букафками, а затем в каталог decomp/ скидывает декомпилированный файл (с тем же именем и тем же расширением), в котором все файлы типа .class заменены декомпилированными файлами типа .java. Обычно я сразу распаковываю полученный jar с исходниками в каталог decomp/, в результате получаю каталог с именем исходного файла и набором распакованных исходников в нем. Очень удобно копаться, не нужно каждый раз лазить в архив.
Первое, что вы должны декомпилировать, будет сам баккит (bukkit.jar), либо spigot.jar, если вы используете данную модификацию. Процесс это неппыстрый, минут 20 на покурить-оправиться у вас точно будет. Зато потом вы получите неплохой справочник по внутренностям вашего сервера, причем в текущей рабочей версии. Поверьте, это вам не раз еще пригодится для понимания особо сложных моментов. Кроме того, с помощью данной утилиты я частенько декомпилирую различные плагины (не всегда удается добыть исходники, да и иногда проще закинуть его на декомпиляцию, чем искать их в инете, благо обфускацию из плагинописателей почти никто не использует) на предмет посмотреть - как оно там все устроено унутре. Очень познавательное чтиво, надо сказать - почерпнул много ценных идей, чего и вам желаю.
В общем, баккит - зло, но меньшее из имеющихся. Работать с ним можно и нужно. И совсем неплохо было бы уметь это делать.- У меня вопрос к свидетелю, можно? Спасибо. Скажите, подсудимый испытывал личную неприязнь к потерпевшему?
- Да, испытывал... Он мне сказал: "Такую личную неприязнь я испытываю к потерпевшему... что кушать не могу!"
к/ф "Мимино"
В следующий раз, наверное, слегка затронем конфигурационные файлы (yml и json) и перейдем непосредственно к плагинам.- 26.11.2015 20:10
- by vasco