Core Interfaces >=1.21.1 API changed in 26.1
BREAKING
In version 26.1, the IMoveableEntityBlock API has been completely redesigned from NBT-based clearData/setData to data-driven storeData/loadData. Choose the appropriate API based on your target version.
Version Selection
| Minecraft | API | Data Carrier | Documentation Section |
|---|---|---|---|
| 1.21.1 | clearData / setData | CompoundTag | 1.21.1 API |
| 26.1 | storeData / loadData | ValueInput / ValueOutput | 26.1 API current |
1.21.1 API: clearData / setData
1.21.1IMoveableEntityBlock (1.21.1)
Interface that blocks must implement, extending EntityBlock.
public interface IMoveableEntityBlock extends EntityBlock {
/**
* Called when the block is about to be pushed. Returns the block entity data
* that needs to be passed to the movement state.
* @param level The world
* @param pos The current block position (before movement)
* @return The NBT data to preserve; returns an empty CompoundTag by default
*/
default CompoundTag clearData(Level level, BlockPos pos) {
return new CompoundTag();
}
/**
* Called when movement ends or the block entity is finally placed, writing
* the previously extracted data to the destination position.
* @param level The world
* @param pos The destination position
* @param nbt The data returned by clearData
*/
default void setData(Level level, BlockPos pos, CompoundTag nbt) {
}
}clearData Implementation
Called when the block is about to be pushed by a piston. Should extract only the custom data from the block entity that needs to be preserved (rather than serializing the entire block entity), returning the NBT to be transferred:
@Override
public CompoundTag clearData(Level level, BlockPos pos) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof MyBlockEntity myBE) {
CompoundTag tag = new CompoundTag();
tag.putInt("customValue", myBE.customValue);
myBE.saveAdditional(tag, level.registryAccess());
return tag;
}
return new CompoundTag();
}setData Implementation
Called when movement ends and the block entity is created at the new position:
@Override
public void setData(Level level, BlockPos pos, CompoundTag nbt) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof MyBlockEntity myBE) {
myBE.customValue = nbt.getInt("customValue");
myBE.loadAdditional(nbt, level.registryAccess());
myBE.setChanged();
}
}Data Flow (1.21.1)
Movement start:
BlockEntity (source position)
→ IMoveableEntityBlock.clearData() → CompoundTag
→ PistonBaseBlockMixin stores into anvillib$nbt (CompoundTag)
→ IPistonMovingBlockEntityExtension.setData(tag)
→ Temporarily stored in PistonMovingBlockEntity
Movement end:
PistonMovingBlockEntity
→ IPistonMovingBlockEntityExtension.clearData() → CompoundTag
→ IMoveableEntityBlock.setData(pos, tag)
→ Written to the BlockEntity at the destinationIPistonMovingBlockEntityExtension (1.21.1)
public interface IPistonMovingBlockEntityExtension {
@Nullable CompoundTag anvillib$clearData();
void anvillib$setData(@Nullable CompoundTag nbt);
@Nullable BlockState anvillib$getMoveState();
}26.1 API: storeData / loadData
26.1In version 26.1, the API has migrated from manual NBT operations to NeoForge's native ValueInput/ValueOutput data-driven system.
IMoveableEntityBlock (26.1)
public interface IMoveableEntityBlock extends EntityBlock {
/**
* Called when the block is about to be pushed. Writes the block entity data
* that needs to be preserved to the ValueOutput.
* @param level The world
* @param pos The current block position (before movement)
* @param output Data output (write data via ValueOutput instead of returning CompoundTag)
*/
default void storeData(Level level, BlockPos pos, ValueOutput output) {
}
/**
* Called when movement ends or the block entity is finally placed. Reads
* previously saved data from the ValueInput.
* @param level The world
* @param pos The destination position
* @param input Data input (read data via ValueInput instead of receiving CompoundTag)
*/
default void loadData(Level level, BlockPos pos, ValueInput input) {
}
}storeData Implementation
@Override
public void storeData(Level level, BlockPos pos, ValueOutput output) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof MyBlockEntity myBE) {
// Write data through ValueOutput acceptor methods
// The specific implementation depends on NeoForge 26.1's ValueOutput API
}
}loadData Implementation
@Override
public void loadData(Level level, BlockPos pos, ValueInput input) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof MyBlockEntity myBE) {
// Read data via ValueInput and restore to the block entity
}
}Data Flow (26.1)
Movement start:
BlockEntity (source position)
→ IMoveableEntityBlock.storeData(level, pos, output)
→ output writes to TagValueOutput
→ PistonBaseBlockMixin calls anvillib$nbt.buildResult()
→ Temporarily stored in PistonMovingBlockEntity
Movement end:
PistonMovingBlockEntity
→ IPistonMovingBlockEntityExtension.clearData()
→ IMoveableEntityBlock.loadData(level, pos, input)
→ input reads data from TagValueInput
→ Restores the BlockEntity at the destinationKey Differences
| Aspect | 1.21.1 | 26.1 |
|---|---|---|
| Data carrier | CompoundTag (manual NBT) | ValueInput / ValueOutput (data-driven) |
| Return style | clearData() → CompoundTag | storeData(..., ValueOutput) (write via output parameter) |
| Mixin temp field | CompoundTag anvillib$nbt | TagValueOutput anvillib$nbt |
| Error handling | No specific error reporting | Uses ProblemReporter.ScopedCollector |
| Save completeness | Must manually select fields to save | Can auto-serialize via saveWithFullMetadata |
IPistonMovingBlockEntityExtension (26.1)
The interface signature is consistent with 1.21.1 (clearData/setData method names are preserved), but internal data types have changed from CompoundTag to the data-driven TagValueOutput:
public interface IPistonMovingBlockEntityExtension {
// Method names unchanged; internal implementation uses TagValueOutput
@Nullable TagValueOutput anvillib$clearData();
void anvillib$setData(@Nullable TagValueOutput nbt);
@Nullable BlockState anvillib$getMoveState();
}Mod developers do not need to use this interface directly -- the Mixin layer handles data injection and extraction automatically.
AnvilLibMoveableEntityBlock
26.1New helper class added in 26.1:
public class AnvilLibMoveableEntityBlock {
public static final Logger LOGGER = ...;
}Used by PistonBaseBlockMixin to provide unified logging.
Migration Checklist (1.21.1 → 26.1)
- Replace
clearData(Level, BlockPos) → CompoundTagwithstoreData(Level, BlockPos, ValueOutput) - Replace
setData(Level, BlockPos, CompoundTag)withloadData(Level, BlockPos, ValueInput) - Remove manual NBT operations (
CompoundTag.putInt(), etc.) and use the data-drivenValueOutput/ValueInputAPI instead - Check for compilation errors:
CompoundTagis no longer used as a return/parameter type - If custom Mixins reference the
anvillib$nbtfield, update the field type