Skip to content

Clean up, clarify BlockEntity #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 1.21.x
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions docs/blockentities/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,37 @@ To create a `BlockEntity` and attach it to a `Block`, the `EntityBlock` interfac

## Storing Data within your `BlockEntity`

In order to save data, override the following two methods:
In order to save data a `BlockEntity` must override two methods:

```java
BlockEntity#saveAdditional(CompoundTag tag)
@Override
protected void saveAdditional(CompoundTag tag, Provider provider) {
super.saveAdditional(tag, provider);
tag.putBoolean("something", this.isSomething);
}

BlockEntity#load(CompoundTag tag)
@Override
protected void loadAdditional(CompoundTag tag, Provider provider) {
super.loadAdditional(tag, provider);
if (tag.contains("something")) {
this.isSomething = tag.getBoolean("something")
}
}
```
These methods are called whenever the `LevelChunk` containing the `BlockEntity` gets loaded from/saved to a tag.
Use them to read and write to the fields in your block entity class.

These methods are called whenever the `LevelChunk` containing the `BlockEntity` gets loaded from or saved to disk. Use the `CompoundTag`'s `put*` and `get*` family of methods to ensure your persistent values are serialized.

!!! note
Whenever your data changes, you need to call `BlockEntity#setChanged`; otherwise, the `LevelChunk` containing your `BlockEntity` might be skipped while the level is saved.
Whenever your data changes, you need to call `BlockEntity#setChanged` to mark `LevelChunk` containing your `BlockEntity` "dirty". Otherwise, it might be skipped when the level is saved.

!!! important
It is important that you call the `super` methods!
It is important that you call the `super` methods!

The tag names `id`, `x`, `y`, `z`, `ForgeData` and `ForgeCaps` are reserved by the `super` methods.

## Ticking `BlockEntities`

If you need a ticking `BlockEntity`, for example to keep track of the progress during a smelting process, another method must be implemented and overridden within `EntityBlock`: `EntityBlock#getTicker(Level, BlockState, BlockEntityType)`. This can implement different tickers depending on which logical side the user is on, or just implement one general ticker. In either case, a `BlockEntityTicker` must be returned. Since this is a functional interface, it can just take in a method representing the ticker instead:
If you need a ticking `BlockEntity`, for example to keep track of the progress during a smelting process, override `EntityBlock#getTicker` to return a `BlockEntityTicker`. This can implement different tickers depending on which logical side the user is on, or just implement one general ticker. Since this is a functional interface, it can just take in a lambda or static method representing the ticker instead:

```java
// Inside some Block subclass
Expand Down Expand Up @@ -91,8 +102,8 @@ while the second one processes that data. If your `BlockEntity` doesn't contain

### Synchronizing on Block Update

This method is a bit more complicated, but again you just need to override two or three methods.
Here is a tiny example implementation of it:
This method is a bit more complicated, but again you just need to override two or three methods. Here is a tiny example implementation of it:

```java
@Override
public CompoundTag getUpdateTag() {
Expand All @@ -109,15 +120,18 @@ public Packet<ClientGamePacketListener> getUpdatePacket() {

// Can override IForgeBlockEntity#onDataPacket. By default, this will defer to the #load.
```

The static constructors `ClientboundBlockEntityDataPacket#create` takes:

* The `BlockEntity`.
* An optional function to get the `CompoundTag` from the `BlockEntity`. By default, this uses `BlockEntity#getUpdateTag`.

Now, to send the packet, an update notification must be given on the server.

```java
Level#sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags)
```

The `pos` should be your `BlockEntity`'s position.
For `oldState` and `newState`, you can pass the current `BlockState` at that position.
`flags` is a bitmask that should contain `2`, which will sync the changes to the client. See `Block` for more info as well as the rest of the flags. The flag `2` is equivalent to `Block#UPDATE_CLIENTS`.
Expand Down