Skip to content

Mimic Blocks

satisfy edited this page Oct 9, 2026 · 1 revision

Mimic blocks (net.satisfy.foundation.block.mimic, net.satisfy.foundation.client.mimic)

A mimic block stores another block and shows it. There are two kinds:

  • Retexture: the block takes on the texture of the applied block (Timber Frames, Fireplace Cornice).
  • Held: the block draws the stored block as well (Rope Knot on a fence).

Foundation handles saving, syncing, drops and the models on Fabric and NeoForge. You register your block entity type and say which blocks use which model.

Available since Foundation 1.0.5.

1. Block entity

Register your own type with MimicBlockEntity. The ID stays in your mod, so existing worlds keep working.

public static final RegistrySupplier<BlockEntityType<MimicBlockEntity>> FRAME = BLOCK_ENTITY_TYPES.register("timber_frame",
        () -> BlockEntityType.Builder.of((pos, state) -> new MimicBlockEntity(FRAME.get(), pos, state), TIMBER_FRAME.get()).build(null));
Method Does
getMimicState() the stored block, null when empty
hasMimic() true when something is stored
setMimicState(state) stores the block, syncs to clients and re-renders; null or air clears it

Saved under Mimic. The old Held key (Rope Knot) is still read.

2. Block

public class FrameBlock extends Block implements EntityBlock {
    public static final BooleanProperty APPLIED = MimicBlock.APPLIED;

    @Override
    public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
        return new MimicBlockEntity(MyBlockEntities.FRAME.get(), pos, state);
    }

    @Override
    public @NotNull ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
        return MimicBlock.use(stack, state, level, pos, player, mimic -> true);
    }

    @Override
    protected @NotNull List<ItemStack> getDrops(BlockState state, LootParams.Builder builder) {
        return MimicBlock.addDrops(super.getDrops(state, builder), builder);
    }
}

MimicBlock.use covers everything the player does:

  • Right-click with a full block: applies it, uses up one item (not in creative), sets APPLIED.
  • Right-click with a pickaxe: gives the block back and clears APPLIED.
  • The last argument filters which blocks are allowed. MimicBlock.canAccept already only allows full, normally rendered blocks without a block entity.

addDrops drops the stored block when the mimic block is broken, also through explosions.

Other helpers: MimicBlock.apply(level, pos, state, mimic) and MimicBlock.remove(level, pos, state, drop).

APPLIED is optional. Blocks without it (Rope Knot) always show their stored block.

3. Models (client)

Register once, early. Use preInitClient, not onInitializeClient, because models are baked before client setup on NeoForge.

MimicModels.retexture(MimicModels.ALL, TIMBER_FOUNDATION);
MimicModels.retexture(MimicModels.placeholder(MimicModels.Mode.UNDERLAY), TIMBER_FRAME);
MimicModels.retexture(MimicModels.tintIndex(1), FIREPLACE_CORNICE);
MimicModels.held(ROPE_KNOT);

A QuadRule decides per quad what happens:

Mode Result
KEEP quad stays as it is
REPLACE quad gets the texture of the stored block
UNDERLAY quad gets the texture and the original is drawn on top (frame beams over the filling)

Ready-made rules:

Rule Replaces
MimicModels.ALL every quad
MimicModels.tintIndex(i) quads with tint index i, keeps the rest
MimicModels.placeholder(others) quads whose texture name contains placeholder, others for the rest

Write your own as a lambda: (quad, state) -> ....

Models are matched by block, so all blockstate variants and multipart parts are covered on both loaders. Nothing else to register, no mixin and no ModelLoadingPlugin / ModifyBakingResult in your mod.

Moving an existing mimic block

  1. Delete the own block entity, textured models (Fabric and NeoForge) and the NeoForge getModelData mixin.
  2. Keep the block entity type ID and register it with MimicBlockEntity.
  3. Point APPLIED to MimicBlock.APPLIED (same name applied, blockstates stay valid).
  4. Replace useItemOn and playerWillDestroy with MimicBlock.use and getDrops as above.
  5. Register the model rule in preInitClient.

Clone this wiki locally