Jump to content


- - - - -

Что такое NMS и как с ним бороться?


Прошлой статьей мы начали небольшой цикл на тему написания плагинов для серверов Bukkit. Статья получилась из разряда "Вообще". Сегодня обратимся к конкретике и рассмотрим основы структуры майнкрафтовского сервера.

- Астронавты, которая тут цапа?!
- Там... ржавая гайка, родной.
- У вас все тут ржавое!
- А эта самая ржавая!
к/ф "Кин-дза-дза"

А вот тут как бы сам по себе, из ниоткуда, возник резонный вопрос: "А для чего мы все это делаем, и что это за Баккит такой?". Попробуем потихоньку разобраться во всех этих майновских хитросплетениях, поскольку без хотя бы минимального понимания механизма работы сервера писать для него плагины будет сущим мучением (испытано на себе, не повторять в домашних условиях!).

Итак, майновский сервер представляет собой jar-файл с набором откомпилированных до уровня байт-кода java-классов. Если интересно, можно открыть его каким-нибудь архиватором типа WinRAR или 7-zip (сейчас меня закидают яйцами пингвинов) и посмотреть как это все разложено по каталогам. Основной набор файлов лежит в подкаталоге /net/minecraft/server/v1_X_RY/, где X - мажорная (основная) версия сервера, а Y - минорный (вторичный) номер билда. Например, для майнкрафта версии 1.8.8 этот подкаталог имеет название "v1_8_R3". В подкаталоге находится более 1.5 тысяч файлов с расширением .class (java байт-код), обеспечивающих работу вашего сервера. Минорная версия (билд) меняется не очень часто, например, билд R3 тянется аж (если мне склероз не изменяет) с версии 1.8.4. Мажорный номер, как вы понимаете, меняется еще реже, в соответствии с изменением версии игры (например, переход с версии 1.7.10 на 1.8).
Указанный путь /net/minecraft/server имеет вполне реальный смысл: именно так называется основной java-пакет (package) сервера - net.minecraft.server, обычно в разговоре его называют NMS. Отсюда, когда спрашивают "не изменился ли NMS?", имеют в виду, не сменился ли номер билда v1_X_RY или, не дай бохх, мажорная версия сервера. В основном от этого зависит - будут ли работать плагины, собранные под предыдущую версию, на текущей сборке сервера. Как правило, в 99% случаев, если не было изменений NMS, плагины не требуют адаптации и прекрасно продолжают работать. В случае изменения версии NMS жди проблем: некоторые плагины активно используют низкоуровневый доступ к классам и методам NMS, и, соответственно, начинают сыпаться исключениями, поскольку не могут найти нужный путь (читай, пакет) и загрузить нужный класс. В итоге приходится либо ждать, когда у разработчиков дойдут руки адаптировать свое творение под новую версию, либо искать исходники плагина и делать за них их работу, либо заниматься некромантией: декомпилировать плагин и далее проводить его адаптацию, надеясь что декомпиляция не нарушила его целостность.

Основная проблема, из-за которой начинают глючить плагины при смене версии NMS даже не столько в изменившемся названии пакета (путь к каталогу). Больше всего проблем доставляет так называемая обфускация, которую активно применяют моджанги в попытке скрыть от хитровывернутых любопытствующих исходный код сервера. Смысл процесса обфускации (obfuscation - затемнение) - переименование перед компиляцией (или после нее) имен методов и полей классов в бессмысленные названия. Попробуйте, например, догадаться, что делает метод:
private int e(ItemStack itemstack)
{
...
}

И таких a(), b(), c(), d() и e() в коде майна - три из четырех. Догадаться об их назначении можно лишь после вдумчивого изучения содержимого. А если учесть, что переименовываются не только названия методов, но и названия членов (переменных) класса, задача и вовсе становится нетривиальной. Причем в одном классе запросто может встретиться в дополнение к вышеприведенному метод:
public int e(Item item, int i, int j, NBTTagCompound nbttagcompound)
{
...
}

Как видно, имена у них одинаковые, а выполняют они совершенно разные функции в зависимости от входного набора аргументов [и возвращаемого значения]. Яву это нисколько не смущает, а вот человеку крышу сносит конкретно.

Однако самая большая проблема даже не в этом, а в том, что при смене NMS методы и члены класса свободно могут поменять названия, причем непредсказуемым образом. Слава КПСС, происходит это не очень часто, а если и происходит, в 99% случаев ведет за собой смену набора аргументов, что приводит к появлению в среде разработки (см., Eclipse) ошибок компиляции. После появления и нахождения такой ошибки приходится поднимать исходный код новой версии NMS, искать куда убежал метод, который в прошлой версии назывался, например, int e(ItemStack itemstack), и после обнаружения его под именем int c(ItemStack itemstack) менять ссылку на него в коде плагина. В оставшемся 1% случаев метод меняет название, а на его месте оказывается другой метод, имеющий теперь такое же название и такой же набор аргументов, но выполняющий совершенно другие задачи. В этом случае об ошибке вы узнаете, скорее всего, по крикам "одмин чо за фигня ни паиграть жи!".

Как небольшой костыль можно использовать т.н. Minecraft Coder Pack (MCP), который производит деобфускацию исходников майна. В этом случае вы получите (в 90% случаев) более-менее внятные имена свойств и методов классов. Однако здесь вас ждут еще две проблемы: после компиляции вашего плагина вам необходимо будет произвести его обфускацию, дабы он смог работать с оригинальным jar-Файлом майна. А вторая проблема: разработчики MCP не очень торопятся, новая версия выходит с большой задержкой после обновления NMS. Отсюда столь частые задержки выхода обновленных версий многих известных плагинов, разработчики которых привыкли полагаться на добрых кудесников, придумавших MCP. Бяда, в общем.

Еще пару слов по структуре: такое огромное количество файлов в каталоге MNS (вообще-то там есть еще подкаталог v1_X_RY, но обычно его опускают) объясняется тем, что каждый класс, даже самый мелкий, находится в отдельном файле. Независимо от того, имеет он размер в несколько строк или в несколько тысяч строк. Причем вполне возможно, что на этапе написания кода классы были сгруппированы в файлах по нескольку штук, ява вполне это допускает в пределах одного пакета. Однако на этапе компиляции они в любом случае разбиваются по принципу 1 класс -> 1 файл.

Что интересно, код майнкрафтовского сервера и клиента на уровне классов хоть и схож во многом, но все же разный, если код сервера побросали в один пакет, то код клиента аккуратно разложен по подпакетам и гораздо лучше структурирован. В любом случае, если вы собираетесь писать плагины для сервера, изучать вам придется именно код серверной части, хотя знание клиентской части, конечно, существенно облегчит процесс. В общем и целом, код серверной части с точки зрения соответствия принципам объектно-ориентированного программирования написан на слабую троечку. Причем заявляю я это не потому что "я бы гораздо лучше написал!", просто появление надстройки Bukkit, речь о которой пойдет в следующей части, лучшее доказательство данного тезиса.

Вот, пожалуй, самый минимум того, что необходимо знать об основах т.н. "ванильного" майнкрафтовского вервера, чтобы не пучить глаза, услышав "посмотри в NMS".
  • aleksey_t and Frank like this


0 Comments