Модули
Программистам

Этот раздел посвящен разработке модулей на Java

Загрузка модулей происходит из папки modules. Менеджер модулей открывает по очереди все jar и смотрит на параметр Module-Main-Class в манифесте JAR файла.

Класс модуля - это класс, наследуемый от pro.gravit.launcher.modules.LauncherModule и реализующий метод init(LauncherInitContext initContext)

Реализовывать логику внутри метода init запрещенно. Внутри метода init разрешается обращаться только к методам modulesManager и initContext, при чем при статической загрузке модуля initContext = null, а при динамической загрузке модуля через команду loadModule он будет содержать инстанс контекста, из которого можно получить доступ к LaunchServer

События(евенты)

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

Встроеные общие события находятся в pro.gravit.launcher.modules.event

События лаунчсервера находятся в pro.gravit.launchserver.modules.events. Именно их вы должны использовать при работе с лаунчсервером, так как внутри этих евентов вы получаете инстанс LaunchServer, центральный инстанс всего лаунчсервера, откуда можно добраться до всего

События лаунчера находятся в pro.gravit.launcher.client.events. Именно их вы должны использовать при работе с лаунчером, так как они содержат основные объекты. В отличии от лаунчсервера тут нет как такового "центрального" объекта. Большинство функций статические и получение объекта для их использования не требуется. При этом если вы хотите работать с GUI на 5.1.0+ то вам необходимо еще учесть события модуля Java рантайма

Для подписки на событие вы должны создать метод в вашем классе, принимающий 1 аргумент соответствующий типу евента что вы хотите обрабатывать, после чего в init вы должны вызвать registerEvent, первый аргумент - ваш метод-обработчик, второй аргумент - класс интересующего вас евента

Обратите внимание при загрузке модуля через loadModule евенты, которые уже прошли не вызываются, вместо этого вам передается initContext

Общие возможности API

Хелперы - классы помощники, упростят вам рутиные задачи. Они находятся в pro.gravit.utils.helper и доступны извне при включенном proguard(тоесть вы можете ими пользоваться в майнкрафт клиенте, модах)

Сериализация абстрактных классов или интерфейсов(AuthProvider/AuthHandler и всё что имеет type в сериализованном виде) выполняется с помощью pro.gravit.utils.ProviderMap и pro.gravit.utils.UniversalJsonAdapter доступны извне при включенном proguard

Запросы к лаунчсерверу. Находятся в pro.gravit.launcher.request(ответы в pro.gravit.launcher.events) и доступны извне при включенном proguard. Блокирующий(синхронный) запрос выполняется с помощью создания инстанса нужного Request и выполнения метода request(). Этот метод блокирует поток до того как ответ не придет, а следовательно может серьезно затормозить работу при непраивльном проектировании. Используйте эту возможность с осторожностью
Неблокирующий(асинхронный) заключается в использовании метода request класса StdWebSocketService(5.1.0+), который возвращает не результат, а CompletableFuture, на который вы должны повесить обработку успешного завершения запроса и ошибки(см документацию по CompletableFuture, там очень много всего интересного). Позволяет дождаться завершения нескольких запросов, одного из, проводить любые манипуляции и логику. Для ветки 5.0.X этот код придется реализовывать самостоятельно(см AsyncEventHandler в ранних версиях JavaRuntime) Этот метод рекомендуется к использованию

Подсистема команд. Довольно обширная и интересная система, находится в pro.gravit.utils.command. Поддерживается разделение команд на категории, добавление, удаление команды и замена своей реализацией. Поддерживается автокомплит, подкоманды. Некоторое представление о возможностях этой системы можно получить изучив тесты в LauncherCore

Хеширование директорий, Diff'ы. Находятся в pro.gravit.launcher.hasher и главная их задача - определять разницу между двумя директориями(недостающие/измененные и лишние файлы). На них построена система обновления

Скачивание списка файлов в несколько потоков. pro.gravit.launcher.AsyncDownloader в 5.1.0+ и pro.gravit.launcher.downloader.ListDownloader в 5.0.X позволяют скачать список файлов по http в несколько потоков наиболее оптимальным способом с максимальной скоростью

Основные API лаунчсервера

Ответы на запросы. Находятся в pro.gravit.launchserver.socket.response и определяют всю логику ответа на какой либо запрос. Вы можете зарегистрировать свой Response вызвав WebSocketService.providers.register . Для отправки ошибки используется sendError(если ваш Response наследуется от SimpleResponse(рекомендуется)), для отправки результата sendObject. Для получения параметров просто создаете поле нужного типа в вашем Response, сериализация gson сделает свое дело и в момент выполнения все эти поля будут содержать данные полученные из json запроса клиента. Обратите внимание на параметр requestUUID, он используется для определения на какой запрос поступил данный ответ. При наследовании SimpleResponse и использовании sendObject/sendError класс SimpleResponse позаботится об этом за вас. В противном случае будьте внимательны и если что то происходит непонятное откройте wireshark

Работа с подключенными клиентами. События. События можно отправлять и без запроса, для чего необходимо воспользоваться методами WebSocketService(launchserver.nettyServerSocketHandler.nettyServer.service), тут есть методы как для отправки события конкретному клиенту, так и всем подключенным клиентам. Если вам необходимо отправить события выборочно, просмотреть список клиентов или что то подобное - обратитесь к коду pro.gravit.launchserver.command.service.ClientsCommand(получение WebSocketFrameHandler из netty pipeline и оттуда уже доставать информацию). Некоторые ответы на запросы(то что называется RequestEvent) могут быть посланы без запроса, для чего в requestUUID необходимо записать "UUID события"(5.1.0+), который находится в RequestEvent.eventUUID. Это позволяет например удаленно авторизировать клиента, который не посылал запрос(пример из модуля UnsafeCommandPack)

Сборка лаунчера. pipeline. Сборка лаунчера осуществляется на основе pipeline(pro.gravit.launchserver.binary). Pipeline - это последовательный список задач, на вход задаче подают путь к входному файлу, а на выход ожидается путь к выходному файлу. Выходной файла первой задачи является взодным файлом последующей задачи. Это позволяет преобразовывать JAR по своему усмотрению, добавив свой Task в нужное место pipeline. Так же с 5.1.0(?) поддерживается pipeline для EXE файлов.

AuthProvider/AuthHandler/HWIDHandler/TextureProvider всё это можно реализовать самостоятельно, создав класс, отнаследовавшись от Handelr/Provider функционал которого вы хотите реализовать и зарегистрировав свой класс в ProviderMap соответствующего Handler/Provider.

Компоненты. Компоненты - очень удобный способ конфигурировать ваш код. Помимо конфигурирования через modulesConfigManager/JsonSerializable вы можете просто создать компонент и зарегистрировтаь его, в таком случае ваш модуль будет конфигурироваться прямо из основного конфига, а вы получите удобные средства для реализации ваших идей. Код компонентов и их реализация находится в pro.gravit.launchserver.components

Reconfigurable - это интерфейс создания команд для вашего Handler/Provider или компонента. Работает он на механизме подкоманд, для вызова команд Reconfigurable используется команда config (имя компонента) (команда компонента) (аргументы), поддерживается автокомплит. Вам необходимо всего лишь отнаследоваться от интерфейса Reconfigurable и реализовать метод getCommands, где прописать все ваши команды. Если у вас Provider/Handler или компонент команды зарегистрируются автоматически, если же этого не происходит вызовите launchserver.registerObject