Jump to content

TileEntity Multipart model


Recommended Posts

I am in the process of converting my existing blockstate based model to a tileentity due to excessive use of blockstates (4-value EnumProperty for each direction = 4096 + waterlogged = 8192 permutations!)

I have an existing (generated) multipart blockstate json, is there any way to easily convert it to the same model, just using the data from the tileentity instead of the blockstate?


I'm assuming I need to create a custom BakedModel, but I would ideally like to keep the current data-driven approach (one model for each of the 4 enum values).


It's the first time I've started looking into custom models, and I think a TileEntityRenderer is overkill for static models. Please correct me if I'm wrong.

Link to comment
Share on other sites

  1. Make sure the necessary data in your tile entity is known on the client.
  2. Override getModelData in your tile entity and return an immutable object of the data that is needed to determine the model.
  3. Call requestModelDataUpdate client-side on your tile entity instance whenever the data changes.
  4. Make a custom model loader (IModelLoader) and register it (ModelLoaderRegistry.registerLoader in ModelRegistryEvent).
  5. Reference your loader in your model JSON using the "loader" property, set it to the ID of your loader.
  6. Embed the different models in your model JSON, e.g like this:
      "loader": "foo:bar_loader",
      "model1": {
        // content of model 1 json here
      "model2": {
        // content of model 2 json here
  7. In your loader, call JSONUtils.deserializeClass(<modelJsonElement>, "model1", context, BlockModel.class) for all the embedded models, this will give you BlockModel instances for all of them.
  8. Make an implementation of IModelGeometry, this holds the deserialized model data, in your case the embedded BlockModel instances.
    1. In bake() you need to call BlockModel#bakeModel on all embedded models, which will give you an IBakedModel for all of them. Then return your own IBakedModel which stores all those baked models.
    2. In getTextures you need to call getTextures on all the BlockModels you have and return a list of all the results combined.
  9. Make your own implementation of IBakedModel, use IDynamicBakedModel here. This is the implementation you need in step 8.1. Delegate all the methods to the appropriate embedded model, in particular getQuads and getParticleTexture. You can use the model data (from step 2) which you receive as an argument here.

I don't know if this is the optimal approach, but this is how I'd do it.

  • Thanks 1
Link to comment
Share on other sites

Almost fully working.

Only thing I'm now struggling with is forcing my TileEntity to sync.

I'm calling markDirty and requestModelDataUpdate after changing the property, but the client side data doesn't refresh until I close and reopen the world.

Do I need to send a custom packet, or is there an easier way of forcing a resync?

Link to comment
Share on other sites

Even closer now.

I do not want to update neighbours, as the property is changed from Block#neighborChanged, and I do not want to cause an infinite loop.

//Called after updating property
BlockState blockState = getBlockState();
//Do not update neighbours
getWorld().notifyBlockUpdate(getPos(), blockState, blockState, Constants.BlockFlags.BLOCK_UPDATE | Constants.BlockFlags.RERENDER_MAIN_THREAD); 

I also call requestModelDataUpdate at the end of handleUpdateTag.

The data is now correct on the client (displayed when right clicked), but the render is always 1 step behind (Only updates once another block is placed).

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in

Sign In Now

  • Create New...

Important Information

By using this site, you agree to our Terms of Use.