Table of Contents

Точки входа (Энтрипоинты)

Точки входа объявляются в fabric.mod.json мода, чтобы предоставить части кода для использования загрузчиком Fabric или другими модами. В основном они используются для выполнения некоторого кода во время инициализации игры для инициализации модов, хотя система точек входа имеет и другие применения. Точки входа загружаются языковыми адаптерами, которые попытаются создать объект Java указанного типа, используя имя объекта кода.

Точка входа отображается под некоторым именем, ссылается на некоторый объект кода и должна основываться на знакомом прототипе точки входа. Прототип точки входа определяет имя (например, “main” или “client”) и ожидаемый тип объекта, на который должна ссылаться точка входа (например, интерфейс ModInitializer). Поставщик прототипа точки входа объявляет прототип точки входа и отвечает за доступ к точкам входа, а также может указать, как они будут использоваться. Fabric Loader предоставляет некоторые встроенные прототипы точек входа, в то время как моды также могут предоставлять свои собственные.

Точки входа можно считать более мощной реализацией Java Service Provider Interfaces.

Основное использование

Мод может объявлять любое количество точек входа под разными именами в своем fabric.mod.json. “main” используется для инициализации частей мода, которые являются общими как для клиента Minecraft, так и для выделенного сервера, таких как записи регистрации. “client” используется для инициализации частей мода, которые зарезервированы только для клиентов, таких как регистрация объектов, связанных с рендерингом.

{
  [...]
  "entrypoints": {
    "main": [
      "net.fabricmc.ExampleMod"
    ],
    "client": [
      "net.fabricmc.ExampleClientMod"
    ]
  }
  [...]
}

Внимание: Рекомендуется использовать отдельные классы для основных, клиентских и серверных точек входа, чтобы избежать проблем с загрузкой классов. Рассмотрим случай, когда один и тот же класс используется как для основной, так и для клиентской точки входа. При запуске на выделенном сервере, даже если точка входа “client” никогда не загружается, класс, содержащий логику инициализации клиента, будет. Даже если клиентская логика никогда не будет выполнена, простая загрузка кода может вызвать проблемы с загрузкой класса.

Встроенные прототипы точек входа

Fabric Loader предоставляет четыре встроенных прототипа точки входа для инициализации мода, некоторые из которых предназначены для работы с инициализацией по отношению к физическим сторонам (см. Серверная и Клиентские стороны). Основные, клиентские и серверные точки входа загружаются и вызываются на ранней стадии инициализации игры, в момент, когда большинство, но не все игровые системы готовы к модификации. Эти точки входа обычно используются для начальной загрузки модов путем регистрации объектов реестра, прослушивателей событий и других обратных вызовов для выполнения последующих действий.

Все основные точки входа вызываются перед всеми точками входа клиента/сервера. Точное время вызова этих точек входа не указано и может варьироваться в разных версиях игры.

Типы ссылок на код

Ссылка на код точки входа преобразуется в экземпляр типа прототипа точки входа. Наиболее распространенный способ создания точки входа - это ссылка на класс, который реализует ожидаемый тип, но эти ссылки на код могут быть сделаны несколькими способами. Внутренне языковой адаптер отвечает за интерпретацию ссылок и превращение их в экземпляры. Языковой адаптер по умолчанию предназначен для кода Java и, таким образом, поддерживает следующие типы ссылок:

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

Языковые адаптеры для других языков могут быть реализованы модами. fabric-language-kotlin предоставляет языковой адаптер для Kotlin.

Другие приложения точек входа

Моды могут вызывать точки входа друг друга в целях интеграции. Точка входа загружается лениво, когда запрашиваются точки входа для определенного прототипа точки входа, что делает точку входа отличным инструментом для дополнительной интеграции модов. Мод может стать поставщиком прототипа точки входа, объявив, что другие моды должны предоставлять точки входа на основе прототипа точки входа, часто используя класс или интерфейс, который мод предоставляет в своем API. Моды могут безопасно использовать этот класс или интерфейс, даже если поставщик не установлен (что делает класс или интерфейс недоступными), поскольку точки входа загружаются только по запросу. Когда провайдер отсутствует, точка входа будет просто проигнорирована.

Доступ к экземплярам точкам входа можно получить, вызвав FabricLoader#getEntrypointContainers(имя, тип). Это возвращает список контейнеров точки входа. Эти контейнеры содержат экземпляр точки хода и контейнер мода, который предоставил экземпляр. Это может быть использовано модом для определения того, какие моды зарегистрировали точку входа.

Экземпляры точек входа запоминаются по их имени, а также по их типу. Использование одной и той же ссылки на код для нескольких точек входа приведет к созданию нескольких экземпляров. Хотя на практике это крайне абсурдно, если getEntrypoints вызывается несколько раз с одним и тем же именем, но разных типов, экземпляры создаются и запоминаются для каждого типа.

Примечание о порядке загрузки и фазах (или их отсутствии)

Fabric Loader не имеет понятия о порядке загрузки или фазах загрузки. Точки входа инициализатора - это механизм, с помощью которого обычно выполняется большая часть загрузки модов, но независимо от того, был ли вызван инициализатор или нет, не определяет, можно ли считать мод “загруженным”. Таким образом, неразумно ожидать, что мод завершил свои изменения в игре после того, как были вызваны его инициализаторы. Кроме того, порядок, в котором вызываются точки входа, в основном не определен и не может быть изменен. Единственной гарантией является то, что список инициализаторов в файле fabric.mod.json вызывается в том порядке, в котором они объявлены. Fabric Loader также не предоставляет несколько этапов инициализаторов для решения проблемы отсутствия порядка.

Распространенным примером является ожидание того, что мод A должен иметь возможность загружаться после мода B, потому что мод A заменит объект, зарегистрированный модом B. В качестве альтернативы, мод C хочет быть загружен до мода D, потому что мод D сделает что-то в ответ на регистрацию, выполняемую модом C. Это невозможно сделать по двум причинам:

  1. Инициализаторы модов не требуются для представления перехода в “жизненном цикле загрузки модов”, чтобы после вызова инициализатора регистрировались все его объекты реестра.
  2. Порядок, в котором вызываются инициализаторы мод, не определен, и на него нельзя повлиять, чтобы инициализаторы мода A вызывались после инициализаторов мода B или чтобы инициализаторы мода C вызывались перед инициализаторами мода D.

Оставляя в стороне отсутствующую гарантию регистрации всех объектов в инициализаторах, можно утверждать, что поэтому должны быть другие точки входа для выполнения “пре-инициализации” и “пост-инициализации”, чтобы было ощущение порядка. Это создает многофазную схему загрузки, которая на практике создает проблемы с установлением соглашений, для которых операции должны выполняться на каком этапе, неопределенность и несоблюдение этих соглашений, а также выбросы, которые не соответствуют.