Skip to content

Conversation

@Doridian
Copy link

This allows remapping ResourcePool from one key to another via an addon programmatically.

The reasoning behind this is a mod called EveryCompat (or WoodGood): It basically adds "missing" blocks to all types of wood to make sure you can build, say, a bookshelf out of every modded wood type you have.

The issue is, this causes it to generate block state names like everycompat:q/environmental/azalia_bookshelf or everycompat:abnbl/boatload/azalia_boat.

This allows making an addon for example like https://github.com/Doridian/BlueMapEveryCompatCompat which remaps the resources correctly as seen here: https://github.com/Doridian/BlueMapEveryCompatCompat/blob/main/src/main/java/net/doridian/bluemapeverycompatcompat/EveryCompatResourcePack.java#L27

@Doridian Doridian force-pushed the feat/remap-blockstate-resolution branch 3 times, most recently from 93e034a to 075f3e9 Compare November 26, 2025 02:46
@TBlueF
Copy link
Member

TBlueF commented Nov 26, 2025

The currently intended way is a custom BlockRenderer instead. So, you load all the static "actual" blockstates, models and textures at load time, and register a new BlockRendererType that overrides the isFallbackFor() method and checks if the blockstates namespace is - in your case - everycomp.
Then you implement your custom BlockRenderer so it maps the resource paths correctly and then fetches them from the ResourcePack.

A possible implementation of the block-renderer e.g. would be something like this:

public class EveryCompBlockRenderer implements BlockRenderer {

    private final BlockStateModelRenderer blockStateDelegate;
    private final ResourceModelRenderer resourceModelDelegate;

    public EveryCompBlockRenderer(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings) {
        this.blockStateDelegate = new BlockStateModelRenderer(resourcePack, textureGallery, renderSettings);
        this.resourceModelDelegate = new ResourceModelRenderer(resourcePack, textureGallery, renderSettings);
    }

    @Override
    public void render(BlockNeighborhood block, Variant variant, TileModelView tileModel, Color blockColor) {
        BlockState blockState = block.getBlockState();
        Key remappedId = remap(blockState.getId());

        if (remappedId == null){
            // if we can't do any remapping, we forward the call to the resourceModelDelegate (to render a missing-block-model), 
            // using the blockStateDelegate here would lead to an infinite recursion-loop
            resourceModelDelegate.render(block, variant, tileModel, blockColor);
            return;
        }

        // we remap the blockstate with the new blockstate-id and then call the original BlockStateModelRenderer again 
        // to handle rendering the remapped blockstate properly
        BlockState remappedBlockState = new BlockState(remappedId, blockState.getProperties());
        blockStateDelegate.render(block, remappedBlockState, tileModel, blockColor);
    }

    public Key remap(Key src) {
        if (!src.getNamespace().equals("everycomp")) {
            return null;
        }
        String[] segments = src.getValue().split("/", 3);
        if (segments.length < 3) {
            return null;
        }
        return new Key(segments[1], segments[2]);
    }

}

That being said, i do see the issue with this intended approach, which is that the default block-properties would not be generated using the correct block-model. 🤔
So there might be some mapping needed somewhere, however i don't think adding the mapping to the ResourcePools is the correct way. Especially since with your implementation the mapping is done to all resource-pools without knowing which kind of key is even mapped.
E.g. in your usecase, you only need to map blockstate-id's but you are mapping model and texture resourcepaths as well :D

@Doridian
Copy link
Author

E.g. in your usecase, you only need to map blockstate-id's but you are mapping model and texture resourcepaths as well :D

In my use-case specifically, that's alright (everycomp doesn't remap/provide any sub-resources in the other types), but I do agree it might not be the best.
Initially I tried just adding a Type parameter to the remap function, but then I re-discovered you can't refer to generic arguments as Types in Java, so I couldn't just like .remap(T.class, key) inside of ResourcePool.
One way would be to forcibly pass a Class<T> into the constructor of ResourcePool, which seems a little ugly, but that'd allow passing it down (and using Class<T> will make sure you can't pass anything wrong, either)

That said, I couldn't find other good ways to handle the mapping as we call the getBlockState() at least once to access the underlying pool.
So we either need to remove the getter and provide our own getBlockState function (which would break addons using this pool until they update), or override that one ResourcePool at the least.

These are my thoughts anyway, it has been a couple years since I've written Java actively, so, yeah, my thoughts might not be fully accurate for modern-ish Java.

@Doridian Doridian force-pushed the feat/remap-blockstate-resolution branch from 2eb4f63 to 14a1153 Compare November 27, 2025 17:10
@Doridian Doridian force-pushed the feat/remap-blockstate-resolution branch from 14a1153 to aa4f8b4 Compare November 27, 2025 17:11
@Doridian
Copy link
Author

@TBlueF I updated it to pass the Type through as I described above. So now addons can tell what is being remapped.
It is as pretty as I know how to make it.

If you have better ideas on how to solve this, do let me know

@TBlueF
Copy link
Member

TBlueF commented Nov 27, 2025

Sorry, that's not exactly the way i meant it :D
I am looking into an idea that i have to implement this, but i need to double check the implications of that idea first.

E.g. ResourcePath is a subclass of Key, but with your change or the change that i had in mind, ResourcePool#get(Key) and ResourcePool#get(ResourcePath) is no longer guaranteed to return the same result .. meaning that
pool.get(resourcePath) and pool.get((Key) resourcePath) might return different values. Not sure if i like that :D
ResourcePath in it's whole concept is more of a cache, which is only valid within its own ResourcePack .. in the future changes are planned where more than one ResourcePack is possible, i need to check how this change would affect the future plans.

I will look into this once i find a bit more time and will come back to you with the results. :)

@TBlueF
Copy link
Member

TBlueF commented Dec 1, 2025

Implemented with #759

@TBlueF TBlueF closed this Dec 1, 2025
@Doridian Doridian deleted the feat/remap-blockstate-resolution branch December 2, 2025 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants