A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Cancel Create

mc-modding-book / book / 1.12+ / forge / tileentity / base / article.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Cannot retrieve contributors at this time
278 lines (175 sloc) 16.7 KB

  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents Copy raw contents

Copy raw contents

Гайд перевел и дополнил пользователь форума — AustereTony.

В данной статье я предоставляю свой перевод гайда с сайта shadowfacts по работе с TileEntity. Исходники примера доступны тут.

В Майнкрафте класс Block используется для представления не просто единичного блока в мире, а блока как типа. Инстанс (экземпляр) Block содержит свойства для каждого экземпляра вашего блока, существующего в мире. Если мы хотим что бы наш блок содержал уникальные данные для каждого отдельно взятого экземпляра нам нужно использовать TileEntity .

На нашем Майнкрафт сервере ПОЯВИЛСЯ МЯЧ!

Существует распространённый миф что TileEntity это плохо, особенно для производительности. Это не так. Они могут негативно влиять на производительность если они реализованы не умело, как в прочем и любые другие объекты.

Тайлы бывают двух типов: обновляющиеся (ticking) и не обновляющиеся (non-ticking). Обновляющиеся тайлы обновляются каждый игровой тик (обычно 20 раз в секунду). Они влияют на производительность интенсивнее и требуют аккуратной реализации. Не обновляющиеся тайлы существуют для простого хранения данных и в этом туториале мы создадим простой, не обновляющийся TileEntity .

Прежде чем мы создадим тайл, мы добавим класс, который упростит их создание в будущем.

В первую очередь создадим класс BlockTileEntity :

Этот класс предоставляет нам следующее:

  • Он расширяет BaseBlock , содержащий нашу удобную базовую реализацию Block

// ru.austeretony.counter.block.base.BaseBlock.java public class BaseBlock extends Block < public BaseBlock(String name, Material material, float hardness, float resistanse, SoundType soundType) < super(material); this.setRegistryName(name); this.setUnlocalizedName(name); this.setHardness(hardness); this.setResistance(resistanse); this.setSoundType(soundType); > >

  • Он содержит обобщение, представляющее наш класс TileEntity . Он будет использован для создания вспомогательных средств для уменьшения количества необходимых приведений типов для получения экземпляра нашего тайла на определённой позиции и для уверенности в том, что созданный тайл имеет корректный тип.
  • Он переопределяет hasTileEntity(IBlockState) из Block и возвращает true . Это даёт понять Майнкрафту что у нашего блока есть тайл и его нужно создать.
  • Он содежит два абстрактных метода: 1) getTileEntityClass() — тут мы будем возвращать класс нашего TileEntity , что позволит ему быть зарегистрированным вместе с блоком, 2) createTileEntity() — более специфичная версия стандартного метода из Block . Майнкрафт вызывает его каждый раз когда нужно создать новый экземпляр тайла, например когда мы устанавливаем наш блок.

Теперь когда у нас есть удобная основа самое время создать блок.

УБИЙЦА RUSTME! СРАВНЕНИЕ СЕРВЕРОВ РАСТА В МАЙНКРАФТЕ RUSTEX REMAKE RUSTME RUST

Создадим BlockCounter , расширяющий BlockTileEntity .

Наш блок расширяет BlockTileEntity и содержит обобщение TileEntityCounter (который предстоит создать), так как этот тайл принадлежит этому блоку.

В конструкторе мы просто напросто вызываем суперконструктор и передаём туда все параметры, которые будем указывать при создании блока.

В методе getTileEntityClass() вернём TileEntityCounter.class (мы ещё создадим его). Это позволит зарегистрировать его ассоциировав с именем блока.

В createTileEntity() мы возвращаем новый экземпляр класса TileEntityCounter .

Ну и в конце концов в onBlockActivated() , вызываемый при правом клике по блоку мы делаем следующее:

  • Проверяем, что действия производятся на сервере (это очень важно при работе с TileEntity !).
  • Достаём экземпляр TileEntityCounter .
  • Если игрок кликнул по нижней стороне — уменьшаем значение счётчика.
  • Если клик по верхней стороне — увеличиваем значение.
  • Отправляем сообщение в чат, содержащие значение счётчика.
  • Возвращаем true .
Читайте также:  Вылетает minecraft Windows 10 edition

Разделение: клиент и сервер

Как было подмечено выше, перед тем как работать со счётчиком тайла, мы должны удостовериться, что действия происходят на сервере. Делаем мы это потому как в Майнкрафте клиент и сервер полностью разделены и некоторые методы вызываются для обеих сторон.

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

В одиночной игре клиент и сервер тоже разделены даже несмотря на то, что они исполняются на одном компьютере (на одной JVM, но в разных потоках). В одиночной игре клиент подключается к локальному, приватному серверу, функции которого схожи с физическим сервером. В этом случае серверный поток отражает логический сервер, а клиентский поток отражает логический клиент, так как обе логические стороны выполняются на одной физической стороне.

Поле World.isRemote используется для проверки стороны, на которой происходит выполнение (будь она логической или физической). Оно равно true для физического клиента в многопользовательской игре и для логического клиента в одиночной игре. Это поле равно false для физического сервера в многопользовательской игре и для логического сервера в одиночной.

Делая проверку в условии !world.isRemote , мы обеспечиваем уверенность что действия будут производится на сервере (физическом или логическом).

Создание тайла для блока

Теперь когда у нас есть блок, мы должны создать тайл для него.

Создадим класс TileEntityCounter :

Содержимое весьма не хитрое:

  • Класс расширяет стандартный TileEntity
  • Содержит приватное поле count , содержащее значение счётчика.
  • Переопределяет writeToNBT() и readFromNBT() что бы наши данные были сохранены и загружены с диска.
  • Предоставляет методы getCount() , incrementCount() , decrementCount() для работы с полем count . Кроме того, в методах, изменяющих значение счётчика ( count ) происходит вызов markDirty() из TileEntity . Он даёт игре понять что данные были изменены с момента последнего сохранения и их нужно перезаписать.

Данный формат используется для хранения данных в виде пар ключ-значение, которые легко сериализуется в байты и сохраняется на диск. Вы можете ознакомиться с классом NBTTagCompound для представления о типах данных, которые он может хранить. Ванильный код содержит множество хороших примеров по сохранению и чтению сложных структур данных.

В нашем случае мы сохраняем значение поля count как целочисленный тип Integer с ключём в виде строки » count » в NBT в методе writeToNBT() и читаем его в readFromNBT() .

Тайл нашего блока нужно зарегистрировать. Делать мы это будем там же, где регистрируем наши блоки.

Регистрацию будем производить через вызов GameRegistry.registerTileEntity() и передавать в него класс тайла и его регистрационное имя. Это требуется для того, что бы при загрузке Майнкрафт определял какой TileEntity к какому блоку принадлежит и пересоздавал его загружая из NBT.

Не забывайте, регистрация Block , ItemBlock и TileEntity происходит во время преинициализации в CommonProxy , а регистрация рендера для ItemBlock происходит в процессе преинициализации в ClientProxy (для рендера через ModelLoader ).

Теперь, когда все сделано, запускаем игру и достаем наш блок-счётчик из вкладки «блоки». Установив его, кликните ПКМ по верхней стороне блока — счётчик увеличит значение и в чате появится сообщение.

Читайте также:  Классные дома в Майнкрафте на воде

Увеличение значения счетчика

Нажав ПКМ по нижней стороне несколько раз вы увидите это:

Уменьшение значения счетчика

Клик ПКМ по другим сторонам выведет в чат сообщение с текущим значением счётчика.

Источник: github.com

Tile Entity#

В Майнкрафте класс Block используется для представления не просто единичного блока в мире, а блока как типа. Экземпляр Block содержит свойства для каждого экземпляра блока в мире. Если мы хотим, что бы наш блок содержал уникальные данные/свойстава/GUI для каждого экземпляра, нужно использовать TileEntity .

Тайлы бывают двух типов: обновляющиеся и не обновляющиеся. Не обновляющиеся тайлы просто хранят какую-то информацию и практически не нагружают игровую систему. А вот обновляющиеся тайлы обновляются каждый игровой тик (обычно 20 раз в секунду). Последние влияют на производительность сильнее и требуют очень аккуратной реализации.

Блок#

Создадим CounterBlock , расширяющий Block .

В конструкторе мы просто вызываем конструктор суперкласса super(Properties.create(Material.ROCK)) и передаём туда все пропертисы.

В методе hasTileEntity мы говорим игре, что блок имеет тайл. В createTileEntity мы возвращаем новый экземпляр тайла.

Ну и в конце концов в onBlockActivated , который вызывается при правом клике по блоку мы делаем следующее: * Проверяем, что действия производятся на сервере (это очень важно при работе с TileEntity !). * Достаём экземпляр CounterTile . * Отправляем в чат сообщение, содержащее значение счётчика. * Возвращаем true .

Разделение: клиент и сервер#

Как было подмечено выше, перед тем как работать со счётчиком тайла, мы должны удостовериться, что действия происходят на сервере. Делаем мы это потому, что в Майнкрафте клиент и сервер полностью разделены и некоторые методы вызываются для обеих сторон.

В многопользовательской игре множество клиентов подключены к одному серверу. В этом случае разделение между сторонами очевидно. Сервер, к которому происходит подключение, отражает физический сервер и все отдельные подключенные клиенты являются физическими клиентами. Но в одиночной игре всё немного сложнее.

В одиночной игре клиент и сервер тоже разделены даже несмотря на то, что они исполняются на одном компьютере (на одной JVM, но в разных потоках). В одиночной игре клиент подключается к локальному, приватному серверу, функции которого схожи с физическим сервером. В этом случае серверный поток отражает логический сервер, а клиентский поток отражает логический клиент, так как обе логические стороны выполняются на одной физической стороне.

Поле World.isRemote используется для проверки стороны, на которой происходит выполнение (будь она логической или физической). Оно равно true для физического клиента в многопользовательской игре и для логического клиента в одиночной игре. Это поле равно false для физического сервера в многопользовательской игре и для логического сервера в одиночной.

Делая проверку в условии !world.isRemote , мы обеспечиваем уверенность что действия будут производится на сервере (физическом или логическом).

Создание тайла#

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

public abstract class TutTile extends TileEntity public TutTile(TileEntityType tileEntityTypeIn) super(tileEntityTypeIn); > public CompoundNBT getUpdateTag() return write(new CompoundNBT()); > public abstract SUpdateTileEntityPacket getUpdatePacket(); public abstract void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt); >

* getUpdatePacket возвращает объект пакета синхронизации (уже с данными). Фактически — это аналог write. * onDataPacket — чтение из пакета синхронизации.

Создадим класс CounterTile :

Разбор: * count — количество кликов. * В конструктор суперкласса super(PhoenixTiles.COUNTER.get()) передается зарегистрированный тип тайла. Регистрацию мы сделаем далее. * incCount и getCount — методы работы с кол-вом кликов. * write — сохранение информации в NBT. * read — чтение информации, сохраненной в NBT. * getUpdatePacket — возвращает объект пакета синхронизации(уже с данными). Фактически это аналог write. * onDataPacket — чтение из пакета синхронизации. * UpdatePacket — класс пакета. Далее его разбор: * count хранит кол-во прыжков * readPacketData — чтение из пакета * writePacketData — запись в пакет

Читайте также:  Minecraft 1 4 обновление

NBT формат#

Данный формат используется для хранения данных в виде пар ключ-значение, которые легко сериализуются в байты и сохраняются на диск. Вы можете ознакомиться с классом CompoundNBT для представления о типах данных, которые он может хранить. Ванильный код содержит множество хороших примеров по сохранению и чтению сложных структур данных.

В нашем случае мы сохраняем значение поля count как целочисленный тип Integer с ключём в виде строки » count » в NBT в методе write() и читаем его в read() .

Формат пакетов#

Пакеты используются для синхронизации информации между клиентом и сервером. В отличие от NBT значение получается не по ключу, а читается в той же последовательности, что и пишется. Т. е. если записывается int int BlockPos int, то и читается int int BlockPos int, причём порядок строго сохраняется, независимо от типа данных. Если нарушить порядок — скорее всего вылетит исключение.

Регистрация#

Перед регистрацией тайла, не забудьте зарегистрировать блок. Регистрация тайла почти ничем не отличается от регистрации блока:

public class TutTiles public static final DeferredRegisterTileEntityType> TILE_ENTITIES = new DeferredRegister(ForgeRegistries.TILE_ENTITIES, Tut.MOD_ID); public static final RegistryObjectTileEntityTypeCounterTile>> COUNTER = TILE_ENTITIES.register(«counter», () -> TileEntityType.Builder.create(CounterTile::new, TutBlocks.COUNTER.get()).build(null)); public static void register() TILE_ENTITIES.register(FMLJavaModLoadingContext.get().

getModEventBus()); > >
Не забудем вызвать TutTiles.register() в конструкторе главного класса.

Финал#

Теперь, кликнув на блок, мы увидем какое число содержится в TileEntity.

Источник: mcmodding.ru

Блок-сущность

Блок-сущность (англ. Block entity, ранее тайл-сущность) — блок, имеющий дополнительные данные, связанные с ним, помимо его ID.

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

Блоковые сущности имеют дополнительные данные об определённых блоках, типы которых варьируются в зависимости от блока. В некоторых случаях, эта информация служит для лучшего отображения модели, такие как книга на столе зачаровывания. В других случаях, она используется для хранения предметов внутри объекта. Блоки-сущности можно двигать поршнями.‌ [ только для Bedrock Edition ]

Список блоков с данными

Этот список содержит блоки, имеющие такие данные.

  • Табличка
  • Флаг
  • Сундук
  • Сундук-ловушка
  • Раздатчик
  • Печь
  • Варочная стойка
  • Загрузочная воронка
  • Выбрасыватель
  • Шалкеровый ящик
  • Печи также хранят время до того как текущий предмет будет переплавлен, и время до того, как текущее топливо будет исчерпано.
  • Варочные стойки также хранят время до того, как сварится текущее зелье и время до того, как закончится топливо.
  • Загрузочные воронки также хранят время до следующей транспортировки предмета.
  • Сундуки, сундуки-ловушки и шалкеровые ящики также используют расположение блока для направления анимации открытия и закрытия.
  • Маяк
  • Спаунер мобов
  • какое существо будет спауниться.
  • задержка до того, как существо заспауниться, а также минимальное и максимальное значения для следующей задержки спауна.
  • как много существ должно спауниться на каждую попытку спауна.
  • дополнительная информация о мобе, который должен заспауниться (такие как надетая броня, эффекты и существа, уложенные друг на друге).
  • Также использует позицию блока для отображения вращающегося моба внутри.
  • Музыкальный блок
  • Выдвинутый поршень и блоки, которые он двигает (включая косвенные блоки, такие как блок слизи).
  • Проигрыватель
  • Стол зачаровывания
  • Блок портала Края
  • Сундук Края
  • Голова
  • Командный блок
  • Блок врат Края
  • Структурный блок
  • Ядро реактора Нижнего мира
  • Датчик дневного света
  • Цветочный горшок
  • Компаратор
  • Кровать
  • Улей

Источник: minecraft.fandom.com