[Ⅰ-Ⅱ части] Обучающий API-интерфейс Zero CORE Multiblock

Перевод [Ⅰ-Ⅱ части] Обучающий API-интерфейс Zero CORE Multiblock

Версия(и) Minecraft
1.8 - 1.10
Источник
http://zerono.it/zerocore-multiblock-api-tutorial/
ZeroCore-мод с обучающим API-интерфейсом , предназначенный для других авторов модов​

Он содержит код для обработки основной механики игры и Forge, в том числе:​

  • Обработчики для обработки сетевых сообщений в нужном потоке
  • Базовая реализация TileEntity, упрощающая синхронизацию данных между сервером и удаленными клиентами, обновления и управление графическим интерфейсом
  • Вспомогательные классы и свойства blockstate для обработки связанных текстур
  • Стандартный обработчик конфигурации со встроенной поддержкой изменений в игре.
  • Стандартный генератор руды с поддержкой белого списка. Руды и белый список могут быть изменены во время работы игры для поддержки изменений внутриигровых конфигураций.
Он также является базой для пересмотренного многоблочного API, поддерживающего несколько​
клиентских модов без ограничений производительности.


Обучающий API-интерфейс Zero CORE Multiblock
Zero CORE поставляется с пересмотренной версией Erogenous Beef Multiblock API - базы на которой собственно собран мод Big Reactors/Extreme Reactors. В то время как я работал над портированием API на Minecraft 1.8, 1.9.4 и теперь 1.10.2, я взял на себя смелость немного изменить его, чтобы адаптировать его к новым версиям Minecraft и заставить его работать плавно с несколькими модами клиента, используя его одновременно.​

Это учебник о том, как использовать API для создания многоблочных машин в вашем собственном моде, так же, как я использую его в своем модификаций Big Reactors/Extreme Reactors для Minecraft 1.9.4 и 1.10.2.​

Учебник основан на тестовой многоблочной машине, которую я сделал, когда тестировал свою версию API. Полный исходный код моего тестового примера доступен на моем аккаунте GitHub по адресу: ZeroNoRyouki/ZeroTest

В этом уроке вы узнаете:​

  • Как добавить Zero CORE в свой собственный проект.
  • Что такое многоблочная машина и как работает многоблочный API.
  • Многоблочный контроллер.
  • TileEntity многоблочной машины.
  • Блоки многоблочной машины.
    • Могущественная печь ([Вторая часть])
[Первая часть]

Как добавить Zero CORE в свой собственный проект
Добавление Zero CORE в ваш проект довольно просто. Единственные реальные потребности в том, что ваш проект мода Minecraft Forge для Minecraft 1.9.4 или 1.10.2 (или более новой версии).​

Откройте файл build.gradle в своей среде IDE или в простом текстовом редакторе и найдите раздел «repositories» в разделе «buildscript». Если он есть, добавьте ссылку на мой репозиторий maven внутри него:​
Gradle (Groovy):
maven {
    name "zerocore" url "http://maven.zerono.it/"
}
Если у вас нет раздела «repositories» в разделе «buildscript», добавьте его так же после раздела «minecraft»:​
Gradle (Groovy):
repositories {
    maven {
        name "zerocore" url "http://maven.zerono.it/"
    }
}
На этом этапе мы в основном рассказываем о ForgeGradle, и где искать Zero CORE.​

Теперь, обратите внимание на раздел «dependencies» вне «buildscript». Если у вас он есть, добавьте в него следующую строку:​
Gradle (Groovy):
compile group: "it.zerono.mods.zercore", name: "zerocore", version: "1.10.2-0.0.4"
Если у вас нет раздела «dependencies» за пределами «buildscript», добавьте его после раздела «repositories», который Вы использовали или создали на предыдущих шагах:​
Gradle (Groovy):
dependencies {

    compile group: "it.zerono.mods.zerocore", name: "zerocore", version: "1.10.2-0.0.4"

}
Если вы хотите другую версию Zero CORE, просто измените версию в директиве «compile». Полный список доступных версии можно найти на моем репозитории Maven в maven.zerono.it - /

Сохраните файл build.gradle и перезапустите вашу IDE.​


Что такое многоблочная машина и как работает многоблочный API

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

Для создания многоблочной машины с этим API необходимо три компонента:​

  • Сами блоки, чтобы построить машину. У вас может быть только один тип блоков или разные типы, это полностью зависит от вас и того, что у ваш за мод/машина.
  • TileEntity блоков. Каждый блок машины должен создать специальный, не тикающий TileEntity, который используется для реализации функциональности одного типа блока (если он есть) и для связывания блока вместе как одно целое (как один большой блок).
  • Многоблочный контроллер. Это особый класс, который отвечает за всю машину. Вы действительно можете сказать, что контроллер - это машина: он диктует, когда машина считается собранной, что может быть частью машины или нет (проверка, совместно с TileEntity ) и, самое главное, то, что машина фактически делает.
Многоблочный API предоставляет своим пользователям базовый класс для TileEntity многоблочной структуры (MultiblockTileEntityBase) и один для контроллера (MultiblockControllerBase). Что вам нужно сделать, так это создать свои собственные элементы много блочной структуры и контроллеры элементов многоблочной структуры, расширяя эти базовые классы и добавляя блоки для этих элементов многоблочной структуры.​
Каждая многоблочная часть и контроллеры отслеживаются многоблочным реестром, который будет координировать создание или уничтожение всей машины, вызывая различные события на контроллерах или самих TileEntity.​
Многоблочная машина может быть выполнена в любой форме, вам просто нужен класс контроллера и класс TileEntity, который проверяет выбранную форму. API поставляется с готовыми к запуску классами для многоблочной структуры (RectangularMultiblockTileEntityBase и RectangularMultiblockControllerBase).​
Многоблочный API предоставляет своим пользователям базовый класс для объекта многоблочной черепицы (MultiblockTileEntityBase) и один для контроллера (MultiblockControllerBase). Что вам нужно сделать, так это создать свои собственные элементы и контроллеры элементов, расширяя эти базовые классы и добавляя блоки для этих элементов.​
Под капотом каждая многоблочная часть и контроллеры отслеживаются многоблочным реестром, который будет координировать создание или уничтожение всей машины, повышая различные события на контроллерах или самих элементах плитки.​
Многоблочная машина может быть выполнена в любой форме, какой вам нравится, вам просто нужен класс контроллера и класс сущности плитки, который проверяет выбранную вами форму. API поставляется с готовыми к запуску классами для прямоугольной машины (RectangularMultiblockTileEntityBase и RectangularMultiblockControllerBase).​
В нашем примере mod мы создадим трехблочную «печь» 3x3x3, состоящую из входного порта питания, чтобы обеспечить мощность машины, входной порт инвентаря для подачи предметов для плавки, выходной порт для извлечения плавленого продукта и «стены», блок, который делает основную часть машины.​
Пожалуйста, имейте в виду, что я расскажу только о многоблочном аспекте машины, а не о том, как заставить печь работать.​
Многоблочный контроллер

Контроллер - это мозг многоблочной машины. Это он диктует, что можно использовать в многоблочной машине, каковы форма и размер машины и что машина действительно делает после сборки.​
Вы создаете свой собственный контроллер, расширяя MultiblockControllerBase. Если машина будет иметь прямоугольную форму, просто добавьте​
RectangularMultiblockControllerBase
- Детали машин и проверки​
Одной из функций контроллера является проверка машины, когда она собирается и собрана.​
Существует несколько методов (абстрактных или нет), которые необходимо реализовать или переопределить для описания базовой формы машины для API.​
Java:
protected abstract int getMinimumNumberOfBlocksForAssembledMachine();
Вы должны вернуть минимальное количество блоков, необходимое для того, чтобы он считался собранным.​
Java:
protected abstract int getMaximumXSize();

protected abstract int getMaximumZSize();

protected abstract int getMaximumYSize();
Максимальный размер машины в данном направлении.​
Java:
protected int getMinimumXSize()

protected int getMinimumYSize()

protected int getMinimumZSize()
Минимальный размер многоблочной структуры в заданном направлении. По умолчанию они возвращают 1.​
Java:
protected abstract boolean isMachineWhole(IMultiblockValidator validatorCallback)
Здесь вы проверяете, правильно ли построена ваша машина с вашей собственной логикой. Вы должны вернуть true, если машина построена правильно. Если есть какие-либо проблемы, вы должны использовать validatorCallback, чтобы установить ошибку для игрока (вы можете получить его с помощью GetLastError ()), прежде чем вернуть false.​
Если вы расширите RectangularMultiblockControllerBase и переопределите этот метод, обязательно вызовите его в суперклассе ПЕРЕД выполнением собственной логики.​
Все контроллеры отслеживать связанные с ними детали: вы можете получить к ним доступ с помощью поля connectedParts контроллера. Когда деталь добавляется или удаляется игроком, контроллер уведомляется через методы onBlockAdded () и onBlockRemoved ().​
Когда состояние сборки машины изменяется, вызываются следующие методы:​
onMachine Assembled (): ранее разобранная машина теперь собрана.​
onMachineDisassembled (): ранее собранная машина теперь разобрана (например: блок разбит игроком).​
onMachinePaused (): ранее собранная машина теперь приостановлена. Машина приостанавливается из за проблем, связанных с не-игроком (например: выгружается блок).​
onMachineRestored (): ранее приостановленная машина теперь собрана.​
- Проверка блоков без элемента фрагмента.​
Вы можете использовать блоки ванили или, в общем, блоки без элемента, основанного на MultiblockTileEntityBase, как компоненты для вашего компьютера. Чтобы проверить эти блоки, в вашем классе контроллера должны быть реализованы следующие методы. RectangularMultiblockControllerBase автоматически вызывает их, когда такой блок находится внутри машины.​
Java:
protected abstract boolean isBlockGoodForFrame(World world, int x, int y, int z, IMultiblockValidator validatorCallback);
Проверьте, действителен ли блок в данном месте для рамы машины (внешний периметр машины).​
Java:
protected abstract boolean isBlockGoodForTop(World world, int x, int y, int z, IMultiblockValidator validatorCallback);
Проверьте, если блок в указанном месте действительно для машины верхней поверхности.​
Java:
protected abstract boolean isBlockGoodForBottom(World world, int x, int y, int z, IMultiblockValidator validatorCallback);
Проверьте, действителен ли блок в данном месте для сторон машины.​
Java:
protected abstract boolean isBlockGoodForInterior(World world, int x, int y, int z, IMultiblockValidator validatorCallback);
Проверьте, действителен ли блок в данном месте для внутреннего пространства машины​
Для всех из них вы должны вернуть true, если блок действителен или установлен ошибка, и возвращает false.​
- Сохранение и загрузка состояния контроллера / машины.​
Важной концепцией является диспетчер сохранения делегата: на одну из частей, прикрепленных к контроллеру, будет возложена ответственность за сохранение и загрузку внутренних данных контроллера (и машины) вдоль его собственной (если она есть). Все это обрабатывается базовыми классами, вам просто нужно реализовать методы syncDataFrom () для загрузки данных из NBTTagCompound и syncDataTo () для сохранения данных контроллера. Мы поговорим больше об этих двух методах в разделе о TileEntity.​
- Отслеживание контроллера.​
Чтобы выполнить некоторую фактическую работу, вы должны реализовать следующий метод:​
Java:
protected abstract boolean updateServer();
Если машина собрана, то этот метод вызывается, на серверном потоке, каждый тик игры, поэтому старайтесь держать этот код маленьким и быстрым. Возвратите true, если внутреннее состояние контроллера было обновлено.​
Если необходимо выполнить некоторый код на клиенте, переопределите следующий метод:​
Java:
protected abstract void updateClient();
TileEntity многоблочной машины
Каждый элемент многоуровневой многослойной машины должен быть получен из MultiblockTileEntityBase. Для прямоугольной машины вы можете использовать RectangularMultiblockTileEntityBase.​
- Проверка деталей​
TileEntity будет задан, если он действителен для позиции, в которой он находится в данный момент, когда выполняется проверка проверки. Один из методов isGoodFor **** будет вызываться в зависимости от положения TileEntity. Реализация по умолчанию просто возвращает true.​
- Проверка контроллера и создание.​
Вы должны реализовать следующие методы, чтобы API обозначил правильный контроллер для ваших TileEntity.​
Class<? extends MultiblockControllerBase> getMultiblockControllerType(): вы должны вернуть класс вашего контроллера.​
MultiblockControllerBase createNewMultiblock (): при вызове создайте новый экземпляр вашего контроллера.​
Если вам нужно получить доступ к экземпляру контроллера, прикрепленному к вашему TileEntity, вы можете вызвать isConnected (), чтобы проверить, привязан ли элемент структуры к контроллеру, а затем getMultiblockController () для извлечения контроллера.​
- Сохранение и загрузка состояния TileEntity.​
MultiblockTileEntityBase расширяет класс утилиты Zero CORE для собственного стильного элемента (ModTileEntity), чтобы воспользоваться его механизмом синхронизации унифицированного состояния: syncDataTo () и syncDataFrom () реализованы для сохранения данных и загрузки их обратно, когда Minecraft или API многоблока запрашивают его.​
Оба метода получают NBTTagCompound для сохранения/загрузки состояния TileEntity и перечисления SyncReason, которые описывают запрошенную операцию синхронизации.​
Запрос FullSync запрашивает сохранение или загрузку полного состояния TileEntity. Это обычно происходит, когда TileEntity сначала загружается с диска на стороне сервера.​
NetworkUpdate возникает, когда состояние TileEntity требуется для обновления другой стороны с использованием сетевых пакетов или сообщений. Если требуется только часть состояния TileEntity для обновления другой стороны, которую вы могли бы просто отправить, избегая полного обновления.​
- Сохранение делегата.​
Одному из TileEntity многоблочной структуры будет поручено сохранять состояние машины, а также собственное. Этот TileEntity станет делегатом сохранения для всей машины. Все это обрабатывается с помощью многоблочного API, но если это необходимо, вы можете переопределить makeMultiblockSaveDelegate () и forfeitMultiblockSaveDelegate (), чтобы получать уведомления, когда TileEntity выбирается делегатом сохранения или когда он потеряет позицию.​
- TileEntity и прямоугольные машины​
RectangularMultiblockTileEntityBase выставляет позицию элемента плитки на всей машине, и она «обращена наружу», то есть грани частичного блока, которые подвергаются внешнему миру​
getPartPosition () возвращает, где часть находится в машине (интерьер, рамка, углы, лицо и т. д.).​
Блоки многоблочной машины

Фактические блоки машины являются нормальными блоками, которые создают ваши TileEntity при их размещении. Обычно их blockstate составлен с использованием данных, полученных из TileEntity блоков или контроллера машины.​


[Вторая часть]

Могущественная печь
Наш пример машины представляет собой мультиблок 3x3x3, состоящий из следующих частей:​
  • Блок-основание (блоки для сборки основной части машины)
  • Блок-порта-питания для имитации блока, который принимает энергию для машины (всего 1 на машину).
  • Блок порта-ввода для имитации блока, который принимает предметы для плавки в печи (всего 1 на машину).
  • Блок порта-вывода для имитации блока, который будет выводить плавки (только 1 на машину).
Каждый из 3 портов должен быть размещен на одном из боковых поверхностей машины. Верхний или нижний грани находятся вне пределов.​
С точки зрения многоблочной библиотеки вам понадобятся класс контроллера и класс TileEntity для всех ваших блоков. Вы можете реализовать этот класс любым способом. Я выбрал следующий вид:​
  • MightyFurnaceController: контроллер для машины. Поскольку печь будет кубом 3x3x3, я создам контроллер, расширив RectangularMultiblockControllerBase, чтобы воспользоваться встроенной поддержкой для прямоугольных машин.
  • MightyFurnaceTileEntity: базовый класс для TileEntity многоблочной машины, создающих расширение RectangularMultiblockTileEntityBase. Это будет TileEntity для стеновых блоков основания.
  • MightyFurnacePowerTileEntity: TileEntity для порта-питания и вспомогательный класс MightyFurnaceTileEntity.
  • MightyFurnaceIOPortTileEntity: TileEntity для порта ввода и вывода, а также вспомогательный класс MightyFurnaceTileEntity.
  • MightyFurnaceBlockBase: базовый класс для самих блоков.
  • MightyFurnaceBlockWall: блок основания/стены, подкласс MightyFurnaceBlockBase.
  • MightyFurnaceBlockPort: блок для ввода, вывода и порта питания, также подкласс MightyFurnaceBlockBase.
Здесь я остановлюсь только на самом важном в коде классов. Вы можете найти их полную реализацию на ZeroNoRyouki/ZeroTest
В нашем симуляторе мы попросим игрока щелкнуть печью, чтобы активировать ее (или отключить ее, если она уже активна). Простой щелчок даст ему последнюю ошибку проверки, если таковая имеется.​

Контроллер Могущественной печи


Давайте начнем с того, какое внутреннее состояние должен держать наш контроллер. Поскольку это, вероятно, будет работать в тесном взаимодействии с TileEntity портов, мы будем держать связь с каждым из них. Затем, чтобы имитировать активное / неактивное состояние печи, мы добавим простое логическое поле.​
Java:
private MightyFurnaceIOPortTileEntity _inputPort;
private MightyFurnaceIOPortTileEntity _outputPort;
private MightyFurnacePowerTileEntity _powerPort;
private boolean _active;
В конструкторе мы устанавливаем все ссылки на null, а логическое - на false
Единственные блоки, разрешенные на наших машинах, - это те, который мы создаем для него, поэтому любые другие блоки, найденные на площади нашей машины, недействительны. Все методы isBlockGoodFor *** просто отбрасывают любые блоки, которые их просят оценить:​
Java:
@Override
protected boolean isBlockGoodForFrame(World world, int x, int y, int z, IMultiblockValidator validatorCallback) {

    validatorCallback.setLastError("Zero COREtest:api.multiblock.validation.invalid_block", x, y, z);
    return false;
}
Мы просто устанавливаем сообщение об ошибке проверки с помощью IMultiblockValidator и возвращаем false. Обратите внимание, что сообщение об ошибке является ключевым перевод, указывая на реальное сообщение, в файле языка:​
JSON:
zerotest:api.multiblock.validation.invalid_block=Block at %1$d, %2$d, %3$d is not valid for the machine
Это позволит вам обеспечить перевод сообщений проверки на всех языках, поддерживаемых вашим модулем​
Наша машина будет кубом 3x3x3 с фиксированным числом блоков 27 блоков, и мы расскажем об этом API, переопределив следующие методы:​
Java:
@Override
protected int getMinimumNumberOfBlocksForAssembledMachine() {return 27;}

@Override
protected int getMaximumXSize() {return MACHINE_SIZE;}

@Override
protected int getMaximumZSize() {return MACHINE_SIZE;}

@Override
protected int getMaximumYSize() {return MACHINE_SIZE;}

@Override
protected int getMinimumXSize() {return MACHINE_SIZE;}

@Override
protected int getMinimumYSize() {return MACHINE_SIZE;}

@Override
protected int getMinimumZSize() {return MACHINE_SIZE;}

private static final int MACHINE_SIZE = 3;

Для того, чтобы проверить нашу машину, мы должны переопределить isMachineWhole ():​
Java:
@Override
protected boolean isMachineWhole(IMultiblockValidator validatorCallback) {

    MightyFurnacePowerTileEntity powerPort = null;
    MightyFurnaceIOPortTileEntity inputPort = null;
    MightyFurnaceIOPortTileEntity outputPort = null;

    if (!super.isMachineWhole(validatorCallback))
        return false;
В этом методе мы будем проверять все блоки, найденные в машине, глядя на наши порты. Прежде чем мы это сделаем, мы должны вызывать isMachineWhole в базовом классе и выходить из строя, если ошибка проверки.​
Для того, чтобы проверить, все блоки, мы перебирать на connectedParts. Благодаря методам isBlockGoodFor ***, которые мы давно определили, мы знали, что единственные TileEntity , которые мы можем найти здесь, являются нашими собственными.​
Java:
for (IMultiblockPart part : this.connectedParts) {

        if (part instanceof MightyFurnacePowerTileEntity) {

            if (null != powerPort) {

                validatorCallback.setLastError("Zero COREtest:api.multiblock.validation.powerport_already_present");
                return false;
            }

            powerPort = (MightyFurnacePowerTileEntity)part;
If the tile entity is a MightyFurnacePowerTileEntity, we check if we already found one and give an error in that case. Такая же проверка выполняется для двух других портов.​
Java:
 else if (part instanceof MightyFurnaceIOPortTileEntity) {

            MightyFurnaceIOPortTileEntity io = (MightyFurnaceIOPortTileEntity) part;
            boolean isInput = io.isInput();

            if (isInput) {

                if (null != inputPort) {

                    validatorCallback.setLastError("Zero COREtest:api.multiblock.validation.inputport_already_present");
                    return false;
                }

                inputPort = io;

            } else {

                if (null != outputPort) {

                    validatorCallback.setLastError("Zero COREtest:api.multiblock.validation.outputport_already_present");
                    return false;
                }

                outputPort = io;
            }
        }
    }
После сканирования всех TileEntity в машине мы устанавливаем ошибку проверки, если какой-либо из трех типов TileEntity отсутствует.​
Java:
if (null == powerPort) {

        validatorCallback.setLastError("Zero COREtest:api.multiblock.validation.powerport_missing");
        return false;
    }

    if (null == inputPort) {

        validatorCallback.setLastError("Zero COREtest:api.multiblock.validation.inputport_missing");
        return false;
    }

    if (null == outputPort) {

        validatorCallback.setLastError("Zero COREtest:api.multiblock.validation.outputport_missing");
        return false;
    }

    return true;
}

Пожалуйста, обратите внимание, что мы только проверяем, есть ли TileEntity портов. Мы еще не храним ссылку на них в наших внутренних полях, ожидая, когда машина будет собрана (или восстановлена):​
Java:
@Override
protected void onMachineAssembled() {

    this.lookupPorts();

    if (WorldHelper.calledByLogicalClient(this.WORLD))
        //На клиенте принудительно обновить рендер
        this.markMultiblockForRenderUpdate();
}

Примечание к Zero CORE 1.10.2-0.1.0.1 +
Начиная с версии 1.10.2-0.1.0.1, методы onMachineAssembled () и onMachineBroken () являются устаревшими.​

Для их замены были добавлены новые методы:​

  • onPreMachineAssembled
  • onPreMachineBroken
  • onPostMachineAssembled
  • onPostMachineBroken
Новые методы вызываются непосредственно перед (Pre) машина собрана/сломана и сразу же после (Post) машина собрана/сломана.​

В большинстве случаев то, что вы делали в onMachineAssembled/onMachineBroken, должно быть перенесено в методы эквивалентов Post.​

Метод lookupPorts () просто сканирует вновь найденные в машине элементы структуры, а также сохраняет ссылку на три типа портов во внутренних данных контроллера. Мы также просим клиента повторно отобразить весь мультиблок, поскольку у нас есть другая текстура для наших блоков, когда они собраны вместе.​
Если один из трех портов удален, нам необходимо отключить нашу внутреннюю ссылку, чтобы убедиться, что мы не работаем с удаленным элементом структуры (а также не удалять сбор данных). Для этого мы переопределяем onBlockRemoved ():​
Java:
@Override
protected void onBlockRemoved(IMultiblockPart oldPart) {

    if (oldPart instanceof MightyFurnacePowerTileEntity) {

        MightyFurnacePowerTileEntity tile = (MightyFurnacePowerTileEntity)oldPart;

        if (this._powerPort == tile)
            this._powerPort = null;

    } else if (oldPart instanceof MightyFurnaceIOPortTileEntity) {

        MightyFurnaceIOPortTileEntity tile = (MightyFurnaceIOPortTileEntity)oldPart;

        if (this._outputPort == tile)
            this._outputPort = null;
        else if (this._inputPort == tile)
            this._inputPort = null;
    }
}

Наконец, для имитации активации машины мы предоставляем общедоступный метод активации или деактивации машины:​
Java:
public void setActive(boolean active) {

    if (this._active == active)
        return;

    //Состояние было изменено
    this._active = active;
После того, как состояние было обновлено (если машина не была в таком состоянии уже), разослать обновления для подключенных клиентов, если мы были вызваны на потоке сервера. На клиенте просто запросите обновление рендеринга.​
Java:
    if (WorldHelper.calledByLogicalServer(this.WORLD)) {

        //На стороне сервера, запросите обновление, которое будет отправлено клиенту
        this.markReferenceCoordForUpdate();
        this.markReferenceCoordDirty();

    } else {

        //На клиенте, запросите обновление рендеринга
        this.markMultiblockForRenderUpdate();
    }
}

public void toggleActive() {
    this.setActive(!this._active);
}
Метод toggleActive () это просто вспомогательный метод, чтобы инвертировать состояние машины.​
Как вы помните, наши TileEntity синхронизируются с помощью методов syncDataFrom() и syncDataTo () из ModTileEntity:​
Java:
@Override
protected void syncDataTo(NBTTagCompound data, ModTileEntity.SyncReason syncReason) {
    data.setBoolean("isActive", this.isActive());
Здесь мы просто сохранение текущего состояния в NBTTagCompound:​
Java:
@Override
protected void syncDataFrom(NBTTagCompound data, ModTileEntity.SyncReason syncReason) {

    if (data.hasKey("isActive"))
        this.setActive(data.getBoolean("isActive"));
Обратите внимание, что мы вызываем setActive (), чтобы установить состояние машины на значение, загруженное из NBTTagCompound: это обеспечит правильное поведение, если мы находимся на стороне сервера (обновите состояние и уведомите все клиенты) или на стороне клиента (запросить обновление рендеринга)​

TileEntity Могущественной печи
Базовый класс TileEntity довольно прост. Нам просто нужно одобрить любой запрос проверки как «стена» части могут быть размещены в любом месте в машине, чтобы все isGoodFor *** методов просто возвращают true.​
Вероятно, самая важная работа этого класса - предоставление информации о правильном контроллере для использования для наших TileEntity:​
Java:
@Override
public Class<? extends MultiblockControllerBase> getMultiblockControllerType() {
    return MightyFurnaceController.class;
}

@Override
public MultiblockControllerBase createNewMultiblock() {
    return new MightyFurnaceController(this.worldObj);
}


PowerTileEntity Могущественной печи и IOPortTileEntity Могущественной печи
Поскольку это всего лишь пример многоблока, эти два TileEntity не делают почти ничего, кроме остановки процесса проверки, если они не помещаются на стороне машины. Они делают это путем переопределения isGoodForFrame (), isGoodForTop (), isGoodForBottom () и isGoodForInterior () и установив ошибку проверки в них:​
Java:
@Override
public boolean isGoodForFrame(IMultiblockValidator validatorCallback) {

    validatorCallback.setLastError(MightyFurnaceIOPortTileEntity.s_invalidPosition);
    return false;
}

private static ValidationError s_invalidPosition = new     ValidationError("Zero COREtest:api.multiblock.validation.ioport_invalid_position");
Поскольку ошибка проверки всегда одинакова во всех четырех методах, мы кэшируем экземпляр ошибки в статическом поле и просто возвращаем его, чтобы избежать создания одного и того же объекта при каждом вызове одного из этих методов.​

BlockBase Могущественной печи

Это довольно стандартный блок-класс, и здесь не так много происходит, кроме создания правильного элемента структуры и ответа на игрока, щелкнувшего по нашим блокам: если игрок нажимает на один из блоков, а подкрадывает, мы инвертируем состояние машины , Если он щелкнет без кражи, мы будем извлекать последнюю ошибку проверки с контроллера машины и отображать ее на проигрывателе:​
Java:
protected MightyFurnaceController getFurnaceController(IBlockAccess world, BlockPos position) {

    MultiblockControllerBase controller = this.getMultiblockController(world, position);

    return controller instanceof MightyFurnaceController ? (MightyFurnaceController)controller : null;
Вышеописанный метод - это просто служебный метод для получения правильно типизированного экземпляра контроллера из нашего TileEntity.​
Java:
@Override
public boolean onBlockActivated(World world, BlockPos position, IBlockState state, EntityPlayer player, EnumHand hand, ItemStack heldItem, EnumFacing side, float hitX, float hitY, float hitZ) {

    if (world.isRemote || (hand != EnumHand.OFF_HAND) || (null != heldItem))
        return false;
Если мы в клиентском потоке, игрок использует левую руку или держит что-то в ней, то мы самоустраняемся и ничего не делаем.​
Java:
MightyFurnaceController controller = this.getFurnaceController(world, position);

    if (null != controller) {

        if (player.isSneaking()) {

            //Переключить состояние машины
            controller.toggleActive();
            return true;
Это довольно прямолинейно: если игрок крадется, мы инвертируем состояние машины.​
Java:
        } else {

            //Отображать любые ошибки проверки

            ValidationError status = controller.getLastError();

            if (null != status) {

                player.addChatMessage(status.getChatMessage());
                return true;
            }
Сначала мы спросим у контроллера, есть ли ошибка проверки. Если у нас есть ошибка, мы добавим сообщение (с правильным переводом) в чат игрока.​
Java:
    }
    return false;
}
Наконец, давайте добавим служебный метод, который переносит World.getTileEntity() для простого извлечения из мира многоблочной детали:​
Java:
protected IMultiblockPart getMultiblockPartAt(IBlockAccess world, BlockPos position) {

    TileEntity te = world.getTileEntity(position);

    return te instanceof IMultiblockPart ? (IMultiblockPart)te : null;
}


BlockPort Могущественной печи

Интересно посмотреть на MightyFurnaceBlockPort, чтобы узнать, как мы могли бы использовать информацию, полученную от TileEntity блока или машинного контроллера, для визуализации нашего блока по-разному, если машина собрана или нет: мы делаем это, переопределяя метод ванили getActualState (). Blockstate порта содержит два свойства:​
  • ASSEMBLED: логическое свойство, которое принимает значение true, когда машина собрана.
  • HFACING: одно из значений EnumFacing, указывающее ориентацию порта.

Метод getActualState () определяется как:​
Java:
@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos position) {

    IMultiblockPart part = this.getMultiblockPartAt(world, position);

    if (part instanceof MightyFurnaceTileEntity) {

        MightyFurnaceTileEntity wallTile = (MightyFurnaceTileEntity)part;
        boolean assembled = wallTile.isConnected() && wallTile.getMultiblockController().isAssembled();

        state = state.withProperty(ASSEMBLED, assembled);
Мы начнем с того, что сначала получим многоблочную часть позиции блока, и, если это одна из наших TileEntity, мы спрашиваем контроллер (если таковой имеется), собрана ли машина или нет, а затем добавим эту информацию в состояние блока/blockstate.​
Затем, если машина собрана, мы проверяем, где наш TileEntity находится в машине, вызвав getPartPosition () и задав свойство HFACING соответствующим образом:​
Java:
        if (assembled) {

            switch (wallTile.getPartPosition()) {

                case NorthFace:
                    state = state.withProperty(HFACING, EnumFacing.NORTH);
                    break;

                case SouthFace:
                    state = state.withProperty(HFACING, EnumFacing.SOUTH);
                    break;

                case WestFace:
                    state = state.withProperty(HFACING, EnumFacing.WEST);
                    break;

                case EastFace:
                    state = state.withProperty(HFACING, EnumFacing.EAST);
                    break;
            }
        }
    }

    return state;
}
Это blockstate JSON файл для порта питания:​
JSON:
{
        "forge_marker": 1,
        "defaults": {
            "textures": {
                "side": "zerotest:blocks/multiblock/mightyfurnace/SingleBlockSimple",
                "particle": "#side",
                "down": "#side",
                "up": "#side",
                "north": "#side",
                "south": "#side",
                "west": "#side",
                "east": "#side"
            },
            "model": "cube"
        },
        "variants": {
            "assembled": {
                "true": {"textures": {"facing": "zerotest:blocks/multiblock/mightyfurnace/FaceCenterPower" }},
                "false": {"textures": {"facing": "zerotest:blocks/multiblock/mightyfurnace/SingleBlockPowerInput" }}
            },
            "hfacing": {
                "north": {"textures": {"north": "#facing" }},
                "south": {"textures": {"south": "#facing" }},
                "west": {"textures": {"west": "#facing" }},
                "east": {"textures": {"east": "#facing" }}
            },
            "inventory": {
                "transform": "forge:default-block",
                "textures": {
                    "north": "zerotest:blocks/multiblock/mightyfurnace/SingleBlockPowerInput"
                }
            }
        }
    }
Выводы
Мы подошли к концу этого урока. Я надеюсь, что это будет полезно для других моддеров и что мы увидим больше многоблочных машин в Minecraft! ?​
Пожалуйста, дайте мне знать, если что-то недостаточно ясно в этом учебнике или если у вас есть вопрос о мультиблочном API или Zero CORE. Меня можно найти в твиттере, под ником @ZeroNoRyouki.​
Автор
Garik
Просмотры
1,098
Первый выпуск
Обновление
Оценка
5.00 звёзд 2 оценок

Другие ресурсы пользователя Garik

Последние обновления

  1. [Ⅰ-Ⅱ части] Обучающий API-интерфейс Zero CORE Multiblock

    Были переведены все комментарий в коде
  2. [Ⅰ-Ⅱ части] Обучающий API-интерфейс Zero CORE Multiblock

    Была полностью переведена статья!

Последние рецензии

Грац, очень интересная статья, пили ещё!
Garik
Garik
Огромное спасибо! Обязательно после болезни продолжу.
полезно
Garik
Garik
Опачки! Спасибо за рецензию!
Сверху