v2.0 Refactor - copycats-plus/copycats GitHub Wiki

Copycats+ version 2.0 includes a complete refactor of the copycat API to separate from Create's messy implementation and create a unified interface for all types of copycat blocks. Due to the massive amount of breaking changes, here's a dedicated document to explain what has changed.

Terminologies

Term Meaning
Material The block state that the copycat is copying, not the state of the copycat itself
Simple copycats Copycats that can only accept one material and extends ICopycatBlock, not IMultiStateCopycatBlock
Multi-state copycats Copycats that can accept multiple materials and extends IMultiStateCopycatBlock, not ICopycatBlock
Kinetic copycats Copycats with moving parts, such as shafts and cogs, that accept materials and are rendered with block entity renderers or with Flywheel instances
Block space A value of 1 equals the length of a whole block
Voxel space A value of 1 equals 1/16 of a block (because most texture packs are 16x16)
Interface with implementation Interfaces such as ICopycatBlock and ICopycatBlockEntity which contain most of the implementation code as default methods. The documentation of these interfaces details the steps to implementing them.
Assembly API A set of helpers to assemble a new model using a given block model and calls to the assemblePiece function. Was called simple copycat API
Functional copycats Obsolete: used to refer to copycats that do not extend from Create's CopycatBlock.

Blocks

Copycat block class hierarchy

All copycat blocks, including multi-state copycats and copycats that extend from Create's CopycatBlock class, now implement ICopycatBlock, so this is the recommended way to check for a copycat block. Multi-state copycats implement ICopycatBlock indirectly via IMultiStateCopycatBlock, which implements copycat block methods with reasonable defaults and provides multi-state specific methods.

When implementing a copycat with a base class other than CCCopycatBlock or MultiStateCopycatBlock, follow the documentation on ICopycatBlock or IMultiStateCopycatBlock interfaces for implementation requirements.

Platform-specific implementations of block events, such as getExplosionResistance, getFriction, addLandingEffects, etc are now handled with mixins. If a copycat block does not inherit from CCCopycatBlock/MultiStateCopycatBlock, it should be registered as a target class for the Forge/Fabric CopycatBlockMixin/MultiStateCopycatBlockMixin.

Block entities

Copycat block entity class hierarchy

All copycat block entities, including multi-state copycats and copycats that extend from Create's CopycatBlockEntity, now implement ICopycatBlockEntity. Multi-state copycats implement ICopycatBlockEntity indirectly via IMultiStateCopycatBlockEntity.

When implementing a copycat with a base class other than CCCopycatBlockEntity or MultiStateCopycatBlockEntity, follow the documentation on ICopycatBlockEntity or IMultiStateCopycatBlockEntity interfaces for implementation requirements.

Platform-specific implementations of block entity render data are now handled with mixins. If a copycat block does not inherit from CCCopycatBlockEntity/MultiStateCopycatBlockEntity, it should be registered as a target class for the Forge/Fabric CopycatBlockEntityMixin/MultiStateCopycatBlockEntityMixin.

Rendering

Copycat models have been reworked and are now separate from Create. There are 2 parts in each copycat model, a platform-specific but block-independent foundation which is named CopycatModelForge / CopycatModelFabric, and a block-specific but platform-independent core which is named Copycat<Block>ModelCore and extends from CopycatModelCore.

Compared with 1.3: SimpleCopycatModel has been merged with CopycatModel, and SimpleCopycatPart has been expanded to become CopycatModelCore.

All copycat models must now use the assembly API exposed by CopycatRenderContext to assemble models. Each CopycatModelCore can specify multiple models to be rendered, with a model getter and an assembly function for each model.

Simple copycats

For simple copycats, the API is largely unchanged. You only need to implement emitCopycatQuads like in SimpleCopycatPart:

public class CopycatSimpleModelCore extends CopycatModelCore {

    @Override
    public void emitCopycatQuads(String key, BlockState state, CopycatRenderContext context, BlockState material) {
        AssemblyTransform transform = t -> t.rotateX(90);
        context.assemblePiece(
                transform,
                vec3(0, 0, 12),
                aabb(16, 1, 4).move(0, 0, 12),
                cull(UP | NORTH)
        );
    }
}

Multi-state copycats

For multi-state copycats, you only need to override registerModels with the provided multi-state helper function, and then assemble the model like in SimpleCopycatPart:

public class YourCopycatMultiStateModelCore extends CopycatModelCore {

    @Override
    public void registerModels(List<ModelEntry> entries) {
        registerForMultiState(entries, CCBlocks.YOUR_COPYCAT_MULTI_STATE.get());
    }

    @Override
    public void emitCopycatQuads(String key, BlockState state, CopycatRenderContext context, BlockState material) {
        context.assemblePiece(
                transform,
                vec3(0, 0, 0),
                aabb(4, layer, 16),
                cull(EAST | UP)
        );
    }
}

Rendering additional models in any copycat

You have two choices if you need to render additional models which are not part of the copied material. You can either do it the vanilla way, where you register the model in the block state of the copycat block using Registrate, or do it with Flywheel partial models.

For the vanilla method, you should first register your model in the block state:

public static final BlockEntry<CopycatGlassFluidPipeBlock> COPYCAT_GLASS_FLUID_PIPE =
            REGISTRATE.block("copycat_glass_fluid_pipe", CopycatGlassFluidPipeBlock::new)
                    / /...
                    .blockstate(CCBlockStateGen::glassPipe)
                    // ...
                    .register();

and then use the model in the copycat model core:

    @Override
    public void registerModels(List<ModelEntry> entries) {
        super.registerModels(entries);
        entries.add(SUPER); // render the block state model without modifications
    }

For the Flywheel PartialModel method, you should first register your model as a partial model (ref Create's AllPartialModels), and then specify it in the copycat model core:

    @Override
    public void registerModels(List<ModelEntry> entries) {
        entries.add(new ModelEntry(
                "my_partial_model",                                          // a custom key to identify your partial model
                (state, mat) -> AllPartialModels.ANALOG_LEVER_HANDLE.get(),  // get the BakedModel from the partial model
                null,                                                        // render the model without modifications
                false                                                        // use the model's logic for culling and CT
        ));
    }

In both cases, provide an implementation of CopycatModelPart if you wish to modify the model using the copycat model assembly API.

Kinetic copycats

To facilitate caching, copycat models for kinetic copycats should be decomposed into reusable parts so that each part can be cached separately. For example, the shaft in a copycat cogwheel should be implemented in a separate CopycatModelCore so that it can be reused in copycat shafts.

To render moving parts for kinetic copycats, first implement a CopycatModelCore for those parts like normal.

Then, instead of registering it with Registrate, add your model core to CopycatPartialModel and specify the block state properties that it requires.

Finally, render the moving parts using KineticCopycatRenderer in your renderer/instance implementation. Take a look at the implementation of copycat shaft for more details.

Misc changes

  • All classes from content.copycat.base are moved to foundation.copycat. APIs in the foundation package are considered public and stable.
  • The multiloader package is removed, with utilities moved to the utility package.
  • GlobalTransform is now renamed to AssemblyTransform. Usage is unchanged.
  • You should now write context.assemblePiece(...) instead of assemblePiece(context, ...).
  • CopycatRenderContext is now an interface with no type arguments, so you can just write CopycatRenderContext instead of CopycatRenderContext<?, ?>.
  • Several variants of assembleQuad are no longer available because they use platform-specific quad classes.
  • Support for block transformations (rotations, mirroring) has been improved. All copycat blocks should implement rotate, mirror and transform. Multi-state copycats should also implement transformStorage.
⚠️ **GitHub.com Fallback** ⚠️