diff --git a/README.md b/README.md index f519278..2fa5f54 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,16 @@ # Continuity -Continuity is a Fabric mod built around modern APIs to allow for the most efficient connected textures experience possible. It is designed to provide full Optifine parity for all resource packs that use the Optifine CTM format. Continuity also supports Optifine-format emissive textures for block and item models. +Continuity is a Fabric mod that allows resource packs that use the OptiFine connected textures format, OptiFine emissive textures format (only for blocks and item models), or OptiFine custom block layers format to work without OptiFine. -Continuity depends on the Fabric API and is client-side only. It includes two built-in resource packs - one provides default connected textures, similar to Optifine, and the other provides a fix for glass pane culling. +Continuity depends on Fabric API and is client-side only. It includes two built-in resource packs. The Default Connected Textures pack provides connected textures for glass, sandstone, and bookshelves, similar to the built-in connected textures provided by OptiFine. The Glass Pane Culling Fix pack culls faces between vertically stacked glass panes to make them look seamless with connected textures. -## Forge Version +Formally, Continuity implements the Continuity connected textures specification, Continuity emissive textures specification, and Continuity custom block layers specification. All of these are extensions of the corresponding OptiFine specification and were created to provide more features to resource pack authors. The documentation for the Continuity specifications can be found at the [Continuity wiki](https://github.com/PepperCode1/Continuity/wiki). -A Forge version of Continuity will not be made for the following reasons. - -- Forge's model API: It is vastly different from Fabric's model API (FRAPI), which is readily utilized by Continuity, and switching to it would not only require an incredible amount of effort, but also result in the mod being extremely inefficient. -- Optifine: It has native support for Forge and provides the same features as Continuity. +An official Forge version of Continuity is not planned at this time due to major technical differences between the Fabric and Forge APIs. An official Forge version of Continuity may be considered if these differences are minimized, possibly via the use of libraries. ### Links -[CurseForge Page](https://www.curseforge.com/minecraft/mc-mods/continuity) - -[Modrinth Page](https://modrinth.com/mod/continuity) - +[CurseForge Page](https://www.curseforge.com/minecraft/mc-mods/continuity) \ +[Modrinth Page](https://modrinth.com/mod/continuity) \ +[Wiki](https://github.com/PepperCode1/Continuity/wiki) \ [Discord](https://discord.gg/7rnTYXu) diff --git a/build.gradle b/build.gradle index 069cf8d..731b75b 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ repositories { url 'https://api.modrinth.com/maven' } maven { - name "VRAM" + name 'VRAM' url 'https://maven.vram.io' } maven { diff --git a/src/main/java/me/pepperbell/continuity/client/mixin/RenderLayersMixin.java b/src/main/java/me/pepperbell/continuity/client/mixin/RenderLayersMixin.java index 96762c4..3ea3d4c 100644 --- a/src/main/java/me/pepperbell/continuity/client/mixin/RenderLayersMixin.java +++ b/src/main/java/me/pepperbell/continuity/client/mixin/RenderLayersMixin.java @@ -15,7 +15,7 @@ public class RenderLayersMixin { @Inject(method = "getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;", at = @At("HEAD"), cancellable = true) private static void continuity$onHeadGetBlockLayer(BlockState state, CallbackInfoReturnable cir) { - if (ContinuityConfig.INSTANCE.customBlockLayers.get()) { + if (!CustomBlockLayers.isEmpty() && ContinuityConfig.INSTANCE.customBlockLayers.get()) { RenderLayer layer = CustomBlockLayers.getLayer(state); if (layer != null) { cir.setReturnValue(layer); @@ -25,7 +25,7 @@ public class RenderLayersMixin { @Inject(method = "getMovingBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;", at = @At("HEAD"), cancellable = true) private static void continuity$onHeadGetMovingBlockLayer(BlockState state, CallbackInfoReturnable cir) { - if (ContinuityConfig.INSTANCE.customBlockLayers.get()) { + if (!CustomBlockLayers.isEmpty() && ContinuityConfig.INSTANCE.customBlockLayers.get()) { RenderLayer layer = CustomBlockLayers.getLayer(state); if (layer != null) { cir.setReturnValue(layer == RenderLayer.getTranslucent() ? RenderLayer.getTranslucentMovingBlock() : layer); diff --git a/src/main/java/me/pepperbell/continuity/client/processor/BaseProcessingPredicate.java b/src/main/java/me/pepperbell/continuity/client/processor/BaseProcessingPredicate.java index b03df17..be49c74 100644 --- a/src/main/java/me/pepperbell/continuity/client/processor/BaseProcessingPredicate.java +++ b/src/main/java/me/pepperbell/continuity/client/processor/BaseProcessingPredicate.java @@ -21,12 +21,16 @@ import net.minecraft.world.biome.Biome; public class BaseProcessingPredicate implements ProcessingPredicate { + @Nullable protected EnumSet faces; + @Nullable protected Predicate biomePredicate; + @Nullable protected IntPredicate heightPredicate; + @Nullable protected Predicate blockEntityNamePredicate; - public BaseProcessingPredicate(EnumSet faces, Predicate biomePredicate, IntPredicate heightPredicate, Predicate blockEntityNamePredicate) { + public BaseProcessingPredicate(@Nullable EnumSet faces, @Nullable Predicate biomePredicate, @Nullable IntPredicate heightPredicate, @Nullable Predicate blockEntityNamePredicate) { this.faces = faces; this.biomePredicate = biomePredicate; this.heightPredicate = heightPredicate; diff --git a/src/main/java/me/pepperbell/continuity/client/processor/CompactCTMQuadProcessor.java b/src/main/java/me/pepperbell/continuity/client/processor/CompactCTMQuadProcessor.java index c22b462..fa38829 100644 --- a/src/main/java/me/pepperbell/continuity/client/processor/CompactCTMQuadProcessor.java +++ b/src/main/java/me/pepperbell/continuity/client/processor/CompactCTMQuadProcessor.java @@ -5,6 +5,7 @@ import java.util.function.Supplier; import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.Nullable; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntMaps; @@ -54,9 +55,10 @@ public class CompactCTMQuadProcessor extends ConnectingQuadProcessor { ArrayUtils.shift(map[7], 1); } + @Nullable protected Sprite[] replacementSprites; - public CompactCTMQuadProcessor(Sprite[] sprites, ProcessingPredicate processingPredicate, ConnectionPredicate connectionPredicate, boolean innerSeams, Sprite[] replacementSprites) { + public CompactCTMQuadProcessor(Sprite[] sprites, ProcessingPredicate processingPredicate, ConnectionPredicate connectionPredicate, boolean innerSeams, @Nullable Sprite[] replacementSprites) { super(sprites, processingPredicate, connectionPredicate, innerSeams); this.replacementSprites = replacementSprites; } @@ -668,7 +670,7 @@ public QuadProcessor createProcessor(CompactConnectingCTMProperties properties, return createProcessor(properties, sprites, replacementSprites); } - public QuadProcessor createProcessor(CompactConnectingCTMProperties properties, Sprite[] sprites, Sprite[] replacementSprites) { + public QuadProcessor createProcessor(CompactConnectingCTMProperties properties, Sprite[] sprites, @Nullable Sprite[] replacementSprites) { return new CompactCTMQuadProcessor(sprites, BaseProcessingPredicate.fromProperties(properties), properties.getConnectionPredicate(), properties.getInnerSeams(), replacementSprites); } diff --git a/src/main/java/me/pepperbell/continuity/client/processor/ProcessingDataKeys.java b/src/main/java/me/pepperbell/continuity/client/processor/ProcessingDataKeys.java index 262fc50..9ae7fb5 100644 --- a/src/main/java/me/pepperbell/continuity/client/processor/ProcessingDataKeys.java +++ b/src/main/java/me/pepperbell/continuity/client/processor/ProcessingDataKeys.java @@ -15,8 +15,8 @@ public final class ProcessingDataKeys { public static final ProcessingDataKey BIOME_CACHE_KEY = create("biome_cache", BaseProcessingPredicate.BiomeCache::new, BaseProcessingPredicate.BiomeCache::reset); public static final ProcessingDataKey BLOCK_ENTITY_NAME_CACHE_KEY = create("block_entity_name_cache", BaseProcessingPredicate.BlockEntityNameCache::new, BaseProcessingPredicate.BlockEntityNameCache::reset); public static final ProcessingDataKey VERTEX_CONTAINER_KEY = create("vertex_container", CompactCTMQuadProcessor.VertexContainer::new); - public static final ProcessingDataKey STANDARD_OVERLAY_RENDERER_POOL_KEY = create("standard_overlay_renderer_pool", StandardOverlayQuadProcessor.OverlayRendererPool::new, StandardOverlayQuadProcessor.OverlayRendererPool::reset); - public static final ProcessingDataKey SIMPLE_OVERLAY_RENDERER_POOL_KEY = create("simple_overlay_renderer_pool", SimpleOverlayQuadProcessor.OverlayRendererPool::new, SimpleOverlayQuadProcessor.OverlayRendererPool::reset); + public static final ProcessingDataKey STANDARD_OVERLAY_EMITTER_POOL_KEY = create("standard_overlay_emitter_pool", StandardOverlayQuadProcessor.OverlayEmitterPool::new, StandardOverlayQuadProcessor.OverlayEmitterPool::reset); + public static final ProcessingDataKey SIMPLE_OVERLAY_EMITTER_POOL_KEY = create("simple_overlay_emitter_pool", SimpleOverlayQuadProcessor.OverlayEmitterPool::new, SimpleOverlayQuadProcessor.OverlayEmitterPool::reset); private static ProcessingDataKey create(String id, Supplier valueSupplier) { return ProcessingDataKeyRegistry.get().registerKey(ContinuityClient.asId(id), valueSupplier); diff --git a/src/main/java/me/pepperbell/continuity/client/processor/overlay/OverlayProcessingPredicate.java b/src/main/java/me/pepperbell/continuity/client/processor/overlay/OverlayProcessingPredicate.java index e45ac41..8a9ad71 100644 --- a/src/main/java/me/pepperbell/continuity/client/processor/overlay/OverlayProcessingPredicate.java +++ b/src/main/java/me/pepperbell/continuity/client/processor/overlay/OverlayProcessingPredicate.java @@ -4,6 +4,8 @@ import java.util.function.IntPredicate; import java.util.function.Predicate; +import org.jetbrains.annotations.Nullable; + import me.pepperbell.continuity.api.client.ProcessingDataProvider; import me.pepperbell.continuity.client.processor.BaseProcessingPredicate; import me.pepperbell.continuity.client.properties.BaseCTMProperties; @@ -17,7 +19,7 @@ import net.minecraft.world.biome.Biome; public class OverlayProcessingPredicate extends BaseProcessingPredicate { - public OverlayProcessingPredicate(EnumSet faces, Predicate biomePredicate, IntPredicate heightPredicate, Predicate blockEntityNamePredicate) { + public OverlayProcessingPredicate(@Nullable EnumSet faces, @Nullable Predicate biomePredicate, @Nullable IntPredicate heightPredicate, @Nullable Predicate blockEntityNamePredicate) { super(faces, biomePredicate, heightPredicate, blockEntityNamePredicate); } diff --git a/src/main/java/me/pepperbell/continuity/client/processor/overlay/SimpleOverlayQuadProcessor.java b/src/main/java/me/pepperbell/continuity/client/processor/overlay/SimpleOverlayQuadProcessor.java index d1b754f..71635db 100644 --- a/src/main/java/me/pepperbell/continuity/client/processor/overlay/SimpleOverlayQuadProcessor.java +++ b/src/main/java/me/pepperbell/continuity/client/processor/overlay/SimpleOverlayQuadProcessor.java @@ -4,6 +4,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.pepperbell.continuity.api.client.QuadProcessor; import me.pepperbell.continuity.client.processor.ProcessingDataKeys; @@ -28,10 +30,11 @@ public class SimpleOverlayQuadProcessor extends SimpleQuadProcessor { protected int tintIndex; + @Nullable protected BlockState tintBlock; protected RenderMaterial material; - public SimpleOverlayQuadProcessor(SpriteProvider spriteProvider, ProcessingPredicate processingPredicate, int tintIndex, BlockState tintBlock, BlendMode layer) { + public SimpleOverlayQuadProcessor(SpriteProvider spriteProvider, ProcessingPredicate processingPredicate, int tintIndex, @Nullable BlockState tintBlock, BlendMode layer) { super(spriteProvider, processingPredicate); this.tintIndex = tintIndex; this.tintBlock = tintBlock; @@ -42,16 +45,16 @@ public SimpleOverlayQuadProcessor(SpriteProvider spriteProvider, ProcessingPredi public ProcessingResult processQuad(MutableQuadView quad, Sprite sprite, BlockRenderView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, int pass, ProcessingContext context) { if (processingPredicate.shouldProcessQuad(quad, sprite, blockView, state, pos, context)) { Sprite newSprite = spriteProvider.getSprite(quad, sprite, blockView, state, pos, randomSupplier, context); - if (!TextureUtil.isMissingSprite(newSprite)) { - OverlayRenderer renderer = context.getData(ProcessingDataKeys.SIMPLE_OVERLAY_RENDERER_POOL_KEY).getRenderer(); - renderer.prepare(quad.lightFace(), newSprite, RenderUtil.getTintColor(tintBlock, blockView, pos, tintIndex), material); - context.addEmitterConsumer(renderer); + if (newSprite != null && !TextureUtil.isMissingSprite(newSprite)) { + OverlayEmitter emitter = context.getData(ProcessingDataKeys.SIMPLE_OVERLAY_EMITTER_POOL_KEY).get(); + emitter.prepare(quad.lightFace(), newSprite, RenderUtil.getTintColor(tintBlock, blockView, pos, tintIndex), material); + context.addEmitterConsumer(emitter); } } return ProcessingResult.CONTINUE; } - public static class OverlayRenderer implements Consumer { + public static class OverlayEmitter implements Consumer { protected Direction face; protected Sprite sprite; protected int color; @@ -70,17 +73,17 @@ public void prepare(Direction face, Sprite sprite, int color, RenderMaterial mat } } - public static class OverlayRendererPool { - protected final List list = new ObjectArrayList<>(); + public static class OverlayEmitterPool { + protected final List list = new ObjectArrayList<>(); protected int nextIndex = 0; - public OverlayRenderer getRenderer() { + public OverlayEmitter get() { if (nextIndex >= list.size()) { - list.add(new OverlayRenderer()); + list.add(new OverlayEmitter()); } - OverlayRenderer renderer = list.get(nextIndex); + OverlayEmitter emitter = list.get(nextIndex); nextIndex++; - return renderer; + return emitter; } public void reset() { diff --git a/src/main/java/me/pepperbell/continuity/client/processor/overlay/StandardOverlayQuadProcessor.java b/src/main/java/me/pepperbell/continuity/client/processor/overlay/StandardOverlayQuadProcessor.java index ea07e10..a874a9c 100644 --- a/src/main/java/me/pepperbell/continuity/client/processor/overlay/StandardOverlayQuadProcessor.java +++ b/src/main/java/me/pepperbell/continuity/client/processor/overlay/StandardOverlayQuadProcessor.java @@ -6,6 +6,8 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.pepperbell.continuity.api.client.ProcessingDataProvider; import me.pepperbell.continuity.api.client.QuadProcessor; @@ -35,17 +37,22 @@ import net.minecraft.world.EmptyBlockView; public class StandardOverlayQuadProcessor extends AbstractQuadProcessor { + @Nullable protected Set matchTilesSet; + @Nullable protected Predicate matchBlocksPredicate; + @Nullable protected Set connectTilesSet; + @Nullable protected Predicate connectBlocksPredicate; protected ConnectionPredicate connectionPredicate; protected int tintIndex; + @Nullable protected BlockState tintBlock; protected RenderMaterial material; - public StandardOverlayQuadProcessor(Sprite[] sprites, ProcessingPredicate processingPredicate, Set matchTilesSet, Predicate matchBlocksPredicate, Set connectTilesSet, Predicate connectBlocksPredicate, ConnectionPredicate connectionPredicate, int tintIndex, BlockState tintBlock, BlendMode layer) { + public StandardOverlayQuadProcessor(Sprite[] sprites, ProcessingPredicate processingPredicate, @Nullable Set matchTilesSet, @Nullable Predicate matchBlocksPredicate, @Nullable Set connectTilesSet, @Nullable Predicate connectBlocksPredicate, ConnectionPredicate connectionPredicate, int tintIndex, @Nullable BlockState tintBlock, BlendMode layer) { super(sprites, processingPredicate); this.matchTilesSet = matchTilesSet; this.matchBlocksPredicate = matchBlocksPredicate; @@ -61,9 +68,9 @@ public StandardOverlayQuadProcessor(Sprite[] sprites, ProcessingPredicate proces @Override public ProcessingResult processQuadInner(MutableQuadView quad, Sprite sprite, BlockRenderView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, int pass, ProcessingContext context) { Direction lightFace = quad.lightFace(); - OverlayRenderer renderer = getRenderer(blockView, pos, state, lightFace, sprite, DirectionMaps.getMap(lightFace)[0], context); - if (renderer != null) { - context.addEmitterConsumer(renderer); + OverlayEmitter emitter = getEmitter(blockView, pos, state, lightFace, sprite, DirectionMaps.getMap(lightFace)[0], context); + if (emitter != null) { + context.addEmitterConsumer(emitter); } return ProcessingResult.CONTINUE; } @@ -130,58 +137,58 @@ protected boolean appliesOverlayCorner(Direction direction0, Direction direction return corner0; } - protected OverlayRenderer fromCorner(Direction direction0, Direction direction1, int sprite0, int sprite1, OverlayRenderer renderer, BlockRenderView blockView, BlockPos pos, BlockState state, Direction lightFace, Sprite quadSprite, BlockPos.Mutable mutablePos) { - Sprite[] rendererSprites = prepareRenderer(renderer, lightFace, blockView, pos); + protected OverlayEmitter fromCorner(Direction direction0, Direction direction1, int sprite0, int sprite1, OverlayEmitter emitter, BlockRenderView blockView, BlockPos pos, BlockState state, Direction lightFace, Sprite quadSprite, BlockPos.Mutable mutablePos) { + prepareEmitter(emitter, lightFace, blockView, pos); + emitter.addSprite(sprites[sprite0]); mutablePos.set(pos, direction0).move(direction1); if (appliesOverlay(blockView.getBlockState(mutablePos), blockView, state, pos, lightFace, quadSprite)) { mutablePos.move(lightFace); if (!blockView.getBlockState(mutablePos).isOpaqueFullCube(blockView, mutablePos)) { - rendererSprites[1] = sprites[sprite1]; + emitter.addSprite(sprites[sprite1]); } } - rendererSprites[0] = sprites[sprite0]; - return renderer; + return emitter; } - protected OverlayRenderer fromOneSide(BlockState state0, BlockState state1, BlockState state2, Direction direction0, Direction direction1, Direction direction2, int sprite0, int sprite1, int sprite2, OverlayRenderer renderer, BlockRenderView blockView, BlockPos pos, BlockState state, Direction lightFace, Sprite quadSprite, BlockPos.Mutable mutablePos) { + protected OverlayEmitter fromOneSide(BlockState state0, BlockState state1, BlockState state2, Direction direction0, Direction direction1, Direction direction2, int sprite0, int sprite1, int sprite2, OverlayEmitter emitter, BlockRenderView blockView, BlockPos pos, BlockState state, Direction lightFace, Sprite quadSprite, BlockPos.Mutable mutablePos) { boolean s0 = hasSameOverlayUnobscured(state0, direction0, blockView, pos, state, lightFace, quadSprite, mutablePos); boolean s1 = hasSameOverlayUnobscured(state1, direction1, blockView, pos, state, lightFace, quadSprite, mutablePos); boolean s2 = hasSameOverlayUnobscured(state2, direction2, blockView, pos, state, lightFace, quadSprite, mutablePos); - Sprite[] rendererSprites = prepareRenderer(renderer, lightFace, blockView, pos); - rendererSprites[0] = sprites[sprite0]; + prepareEmitter(emitter, lightFace, blockView, pos); + emitter.addSprite(sprites[sprite0]); if (s0 | s1) { if (appliesOverlayCorner(direction0, direction1, blockView, pos, state, lightFace, quadSprite, mutablePos)) { - rendererSprites[1] = sprites[sprite1]; + emitter.addSprite(sprites[sprite1]); } } if (s1 | s2) { if (appliesOverlayCorner(direction1, direction2, blockView, pos, state, lightFace, quadSprite, mutablePos)) { - rendererSprites[2] = sprites[sprite2]; + emitter.addSprite(sprites[sprite2]); } } - return renderer; + return emitter; } - protected static OverlayRenderer getRenderer(ProcessingDataProvider dataProvider) { - return dataProvider.getData(ProcessingDataKeys.STANDARD_OVERLAY_RENDERER_POOL_KEY).getRenderer(); + protected static OverlayEmitter getEmitter(ProcessingDataProvider dataProvider) { + return dataProvider.getData(ProcessingDataKeys.STANDARD_OVERLAY_EMITTER_POOL_KEY).get(); } - protected Sprite[] prepareRenderer(OverlayRenderer renderer, Direction face, BlockRenderView blockView, BlockPos pos) { - return renderer.prepare(face, RenderUtil.getTintColor(tintBlock, blockView, pos, tintIndex), material); + protected void prepareEmitter(OverlayEmitter emitter, Direction face, BlockRenderView blockView, BlockPos pos) { + emitter.prepare(face, RenderUtil.getTintColor(tintBlock, blockView, pos, tintIndex), material); } - protected OverlayRenderer prepareRenderer(OverlayRenderer renderer, Direction face, BlockRenderView blockView, BlockPos pos, int sprite1) { - Sprite[] rendererSprites = prepareRenderer(renderer, face, blockView, pos); - rendererSprites[0] = sprites[sprite1]; - return renderer; + protected OverlayEmitter prepareEmitter(OverlayEmitter emitter, Direction face, BlockRenderView blockView, BlockPos pos, int sprite1) { + prepareEmitter(emitter, face, blockView, pos); + emitter.addSprite(sprites[sprite1]); + return emitter; } - protected OverlayRenderer prepareRenderer(OverlayRenderer renderer, Direction face, BlockRenderView blockView, BlockPos pos, int sprite1, int sprite2) { - Sprite[] rendererSprites = prepareRenderer(renderer, face, blockView, pos); - rendererSprites[0] = sprites[sprite1]; - rendererSprites[1] = sprites[sprite2]; - return renderer; + protected OverlayEmitter prepareEmitter(OverlayEmitter emitter, Direction face, BlockRenderView blockView, BlockPos pos, int sprite1, int sprite2) { + prepareEmitter(emitter, face, blockView, pos); + emitter.addSprite(sprites[sprite1]); + emitter.addSprite(sprites[sprite2]); + return emitter; } /* @@ -203,7 +210,8 @@ protected OverlayRenderer prepareRenderer(OverlayRenderer renderer, Direction fa 15: U 16: L U (CORNER) */ - protected OverlayRenderer getRenderer(BlockRenderView blockView, BlockPos pos, BlockState state, Direction lightFace, Sprite quadSprite, Direction[] directions, ProcessingDataProvider dataProvider) { + @Nullable + protected OverlayEmitter getEmitter(BlockRenderView blockView, BlockPos pos, BlockState state, Direction lightFace, Sprite quadSprite, Direction[] directions, ProcessingDataProvider dataProvider) { BlockPos.Mutable mutablePos = dataProvider.getData(ProcessingDataKeys.MUTABLE_POS_KEY); // @@ -224,54 +232,54 @@ protected OverlayRenderer getRenderer(BlockRenderView blockView, BlockPos pos, B // if (left & down & right & up) { - return prepareRenderer(getRenderer(dataProvider), lightFace, blockView, pos, 8); + return prepareEmitter(getEmitter(dataProvider), lightFace, blockView, pos, 8); } if (left & down & right) { - return prepareRenderer(getRenderer(dataProvider), lightFace, blockView, pos, 5); + return prepareEmitter(getEmitter(dataProvider), lightFace, blockView, pos, 5); } if (left & down & up) { - return prepareRenderer(getRenderer(dataProvider), lightFace, blockView, pos, 6); + return prepareEmitter(getEmitter(dataProvider), lightFace, blockView, pos, 6); } if (left & right & up) { - return prepareRenderer(getRenderer(dataProvider), lightFace, blockView, pos, 13); + return prepareEmitter(getEmitter(dataProvider), lightFace, blockView, pos, 13); } if (down & right & up) { - return prepareRenderer(getRenderer(dataProvider), lightFace, blockView, pos, 12); + return prepareEmitter(getEmitter(dataProvider), lightFace, blockView, pos, 12); } if (left & right) { - return prepareRenderer(getRenderer(dataProvider), lightFace, blockView, pos, 9, 7); + return prepareEmitter(getEmitter(dataProvider), lightFace, blockView, pos, 9, 7); } if (up & down) { - return prepareRenderer(getRenderer(dataProvider), lightFace, blockView, pos, 15, 1); + return prepareEmitter(getEmitter(dataProvider), lightFace, blockView, pos, 15, 1); } if (left & down) { - return fromCorner(directions[2], directions[3], 4, 14, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromCorner(directions[2], directions[3], 4, 14, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } if (down & right) { - return fromCorner(directions[0], directions[3], 3, 16, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromCorner(directions[0], directions[3], 3, 16, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } if (right & up) { - return fromCorner(directions[0], directions[1], 10, 2, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromCorner(directions[0], directions[1], 10, 2, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } if (up & left) { - return fromCorner(directions[1], directions[2], 11, 0, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromCorner(directions[1], directions[2], 11, 0, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } // if (left) { - return fromOneSide(state1, state2, state3, directions[1], directions[2], directions[3], 9, 0, 14, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromOneSide(state1, state2, state3, directions[1], directions[2], directions[3], 9, 0, 14, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } if (down) { - return fromOneSide(state2, state3, state0, directions[2], directions[3], directions[0], 1, 14, 16, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromOneSide(state2, state3, state0, directions[2], directions[3], directions[0], 1, 14, 16, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } if (right) { - return fromOneSide(state3, state0, state1, directions[3], directions[0], directions[1], 7, 16, 2, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromOneSide(state3, state0, state1, directions[3], directions[0], directions[1], 7, 16, 2, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } if (up) { - return fromOneSide(state0, state1, state2, directions[0], directions[1], directions[2], 15, 2, 0, getRenderer(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); + return fromOneSide(state0, state1, state2, directions[0], directions[1], directions[2], 15, 2, 0, getEmitter(dataProvider), blockView, pos, state, lightFace, quadSprite, mutablePos); } // @@ -299,21 +307,21 @@ protected OverlayRenderer getRenderer(BlockRenderView blockView, BlockPos pos, B } if (corner0 | corner1 | corner2 | corner3) { - OverlayRenderer renderer = getRenderer(dataProvider); - Sprite[] rendererSprites = prepareRenderer(renderer, lightFace, blockView, pos); + OverlayEmitter emitter = getEmitter(dataProvider); + prepareEmitter(emitter, lightFace, blockView, pos); if (corner0) { - rendererSprites[0] = sprites[2]; + emitter.addSprite(sprites[2]); } if (corner1) { - rendererSprites[1] = sprites[0]; + emitter.addSprite(sprites[0]); } if (corner2) { - rendererSprites[2] = sprites[14]; + emitter.addSprite(sprites[14]); } if (corner3) { - rendererSprites[3] = sprites[16]; + emitter.addSprite(sprites[16]); } - return renderer; + return emitter; } // @@ -321,43 +329,49 @@ protected OverlayRenderer getRenderer(BlockRenderView blockView, BlockPos pos, B return null; } - public static class OverlayRenderer implements Consumer { + public static class OverlayEmitter implements Consumer { protected static final Sprite[] EMPTY_SPRITES = new Sprite[4]; protected Sprite[] sprites = new Sprite[4]; + protected int spriteAmount; protected Direction face; protected int color; protected RenderMaterial material; @Override public void accept(QuadEmitter emitter) { - for (Sprite sprite : sprites) { - if (sprite != null && !TextureUtil.isMissingSprite(sprite)) { - QuadUtil.emitOverlayQuad(emitter, face, sprite, color, material); - } + for (int i = 0; i < spriteAmount; i++) { + QuadUtil.emitOverlayQuad(emitter, face, sprites[i], color, material); } } - public Sprite[] prepare(Direction face, int color, RenderMaterial material) { + public void prepare(Direction face, int color, RenderMaterial material) { System.arraycopy(EMPTY_SPRITES, 0, sprites, 0, EMPTY_SPRITES.length); + spriteAmount = 0; this.face = face; this.color = color; this.material = material; - return sprites; + } + + public void addSprite(Sprite sprite) { + if (sprite != null && !TextureUtil.isMissingSprite(sprite)) { + sprites[spriteAmount] = sprite; + spriteAmount++; + } } } - public static class OverlayRendererPool { - protected final List list = new ObjectArrayList<>(); + public static class OverlayEmitterPool { + protected final List list = new ObjectArrayList<>(); protected int nextIndex = 0; - public OverlayRenderer getRenderer() { + public OverlayEmitter get() { if (nextIndex >= list.size()) { - list.add(new OverlayRenderer()); + list.add(new OverlayEmitter()); } - OverlayRenderer renderer = list.get(nextIndex); + OverlayEmitter emitter = list.get(nextIndex); nextIndex++; - return renderer; + return emitter; } public void reset() { diff --git a/src/main/java/me/pepperbell/continuity/client/properties/BaseCTMProperties.java b/src/main/java/me/pepperbell/continuity/client/properties/BaseCTMProperties.java index f5e73c0..3643256 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/BaseCTMProperties.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/BaseCTMProperties.java @@ -14,9 +14,9 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -29,6 +29,7 @@ import me.pepperbell.continuity.client.util.TextureUtil; import me.pepperbell.continuity.client.util.biome.BiomeHolder; import me.pepperbell.continuity.client.util.biome.BiomeHolderManager; +import me.pepperbell.continuity.client.util.biome.BiomeSetPredicate; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; @@ -59,12 +60,18 @@ public class BaseCTMProperties implements CTMProperties { protected ResourceManager resourceManager; protected String method; + @Nullable protected Set matchTilesSet; + @Nullable protected Predicate matchBlocksPredicate; protected List tiles = Collections.emptyList(); + @Nullable protected EnumSet faces; + @Nullable protected Predicate biomePredicate; + @Nullable protected IntPredicate heightPredicate; + @Nullable protected Predicate blockEntityNamePredicate; protected boolean prioritized = false; @@ -182,89 +189,95 @@ protected void parseTiles() { for (int i = 0; i < tileStrs.length; i++) { String tileStr = tileStrs[i]; - if (!tileStr.isEmpty()) { - if (tileStr.endsWith("") || tileStr.endsWith(".png")) { - listBuilder.add(SPECIAL_SKIP_ID); - continue; - } else if (tileStr.endsWith("") || tileStr.endsWith(".png")) { - listBuilder.add(SPECIAL_DEFAULT_ID); - continue; - } + if (tileStr.isEmpty()) { + continue; + } - String[] rangeParts = tileStr.split("-"); - if (rangeParts.length != 0) { - if (rangeParts.length == 2) { - try { - int min = Integer.parseInt(rangeParts[0]); - int max = Integer.parseInt(rangeParts[1]); - if (min <= max) { + if (tileStr.endsWith("") || tileStr.endsWith(".png")) { + listBuilder.add(SPECIAL_SKIP_ID); + continue; + } else if (tileStr.endsWith("") || tileStr.endsWith(".png")) { + listBuilder.add(SPECIAL_DEFAULT_ID); + continue; + } + + String[] rangeParts = tileStr.split("-", 2); + if (rangeParts.length != 0) { + if (rangeParts.length == 2) { + try { + int min = Integer.parseInt(rangeParts[0]); + int max = Integer.parseInt(rangeParts[1]); + if (min <= max) { + try { for (int tile = min; tile <= max; tile++) { listBuilder.add(new Identifier(id.getNamespace(), basePath + tile + ".png")); } - continue; + } catch (InvalidIdentifierException e) { + ContinuityClient.LOGGER.warn("Invalid 'tiles' element '" + tileStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'", e); } - } catch (NumberFormatException | InvalidIdentifierException e) { - // + } else { + ContinuityClient.LOGGER.warn("Invalid 'tiles' element '" + tileStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); } - } else if (rangeParts.length == 1) { - String[] parts = tileStr.split(":", 2); - if (parts.length != 0) { - String namespace; - String path; - if (parts.length > 1) { - namespace = parts[0]; - path = parts[1]; - } else { - namespace = null; - path = parts[0]; - } - - if (!path.endsWith(".png")) { - path += ".png"; - } + continue; + } catch (NumberFormatException e) { + // + } + } - if (namespace == null) { - if (path.startsWith("assets/minecraft/")) { - path = path.substring(17); - } else if (path.startsWith("./")) { - path = basePath + path.substring(2); - } else if (path.startsWith("~/")) { - path = "optifine/" + path.substring(2); - } else if (path.startsWith("/")) { - path = "optifine/" + path.substring(1); - } + String[] parts = tileStr.split(":", 2); + if (parts.length != 0) { + String namespace; + String path; + if (parts.length > 1) { + namespace = parts[0]; + path = parts[1]; + } else { + namespace = null; + path = parts[0]; + } - if (!path.startsWith("textures/") && !path.startsWith("optifine/")) { - path = basePath + path; - } + if (!path.endsWith(".png")) { + path += ".png"; + } - if (path.startsWith("optifine/")) { - namespace = id.getNamespace(); - } - } else { - if (!path.contains("/")) { - path = "textures/block/" + path; - } else if (!path.startsWith("textures/") && !path.startsWith("optifine/")) { - path = "textures/" + path; - } - } + if (namespace == null) { + if (path.startsWith("assets/minecraft/")) { + path = path.substring(17); + } else if (path.startsWith("./")) { + path = basePath + path.substring(2); + } else if (path.startsWith("~/")) { + path = "optifine/" + path.substring(2); + } else if (path.startsWith("/")) { + path = "optifine/" + path.substring(1); + } - if (namespace == null) { - namespace = Identifier.DEFAULT_NAMESPACE; - } + if (!path.startsWith("textures/") && !path.startsWith("optifine/")) { + path = basePath + path; + } - try { - listBuilder.add(new Identifier(namespace, path)); - continue; - } catch (InvalidIdentifierException e) { - // - } - } else { - continue; + if (path.startsWith("optifine/")) { + namespace = id.getNamespace(); + } + } else { + if (!path.contains("/")) { + path = "textures/block/" + path; + } else if (!path.startsWith("textures/") && !path.startsWith("optifine/")) { + path = "textures/" + path; } } - ContinuityClient.LOGGER.warn("Invalid 'tiles' element '" + tileStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); + + if (namespace == null) { + namespace = Identifier.DEFAULT_NAMESPACE; + } + + try { + listBuilder.add(new Identifier(namespace, path)); + } catch (InvalidIdentifierException e) { + ContinuityClient.LOGGER.warn("Invalid 'tiles' element '" + tileStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'", e); + } } + } else { + ContinuityClient.LOGGER.warn("Invalid 'tiles' element '" + tileStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); } } @@ -284,23 +297,25 @@ protected void parseFaces() { for (int i = 0; i < faceStrs.length; i++) { String faceStr = faceStrs[i]; - if (!faceStr.isEmpty()) { - String faceStr1 = faceStr.toUpperCase(Locale.ROOT); - if (faceStr1.equals("BOTTOM")) { - faces.add(Direction.DOWN); - } else if (faceStr1.equals("TOP")) { - faces.add(Direction.UP); - } else if (faceStr1.equals("SIDES")) { - Iterators.addAll(faces, Direction.Type.HORIZONTAL.iterator()); - } else if (faceStr1.equals("ALL")) { - faces = null; - return; - } else { - try { - faces.add(Direction.valueOf(faceStr1)); - } catch (IllegalArgumentException e) { - ContinuityClient.LOGGER.warn("Unknown 'faces' element '" + faceStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); - } + if (faceStr.isEmpty()) { + continue; + } + + String faceStr1 = faceStr.toUpperCase(Locale.ROOT); + if (faceStr1.equals("BOTTOM")) { + faces.add(Direction.DOWN); + } else if (faceStr1.equals("TOP")) { + faces.add(Direction.UP); + } else if (faceStr1.equals("SIDES")) { + Iterators.addAll(faces, Direction.Type.HORIZONTAL.iterator()); + } else if (faceStr1.equals("ALL")) { + faces = null; + return; + } else { + try { + faces.add(Direction.valueOf(faceStr1)); + } catch (IllegalArgumentException e) { + ContinuityClient.LOGGER.warn("Unknown 'faces' element '" + faceStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); } } } @@ -331,31 +346,25 @@ protected void parseBiomes() { String[] biomeStrs = biomesStr.split(" "); if (biomeStrs.length != 0) { - ImmutableSet.Builder biomeSetBuilder = ImmutableSet.builder(); + ObjectOpenHashSet biomeHolderSet = new ObjectOpenHashSet<>(); for (int i = 0; i < biomeStrs.length; i++) { String biomeStr = biomeStrs[i]; - if (!biomeStr.isEmpty()) { - try { - Identifier biomeId = new Identifier(biomeStr.toLowerCase(Locale.ROOT)); - biomeSetBuilder.add(BiomeHolderManager.getOrCreateHolder(biomeId)); - } catch (InvalidIdentifierException e) { - ContinuityClient.LOGGER.warn("Invalid 'biomes' element '" + biomeStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'", e); - } + if (biomeStr.isEmpty()) { + continue; + } + + try { + Identifier biomeId = new Identifier(biomeStr.toLowerCase(Locale.ROOT)); + biomeHolderSet.add(BiomeHolderManager.getOrCreateHolder(biomeId)); + } catch (InvalidIdentifierException e) { + ContinuityClient.LOGGER.warn("Invalid 'biomes' element '" + biomeStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'", e); } } - ImmutableSet biomeSet = biomeSetBuilder.build(); - if (!biomeSet.isEmpty()) { - BiomeHolder[] biomeArray = biomeSet.toArray(BiomeHolder[]::new); - biomePredicate = biome -> { - for (BiomeHolder holder : biomeArray) { - if (holder.getBiome() == biome) { - return true; - } - } - return false; - }; + if (!biomeHolderSet.isEmpty()) { + biomeHolderSet.trim(); + biomePredicate = new BiomeSetPredicate(biomeHolderSet); if (negate) { biomePredicate = biomePredicate.negate(); } @@ -382,67 +391,66 @@ protected void parseHeights() { String[] heightStrs = heightsStr.trim().split("[ ,]"); if (heightStrs.length != 0) { - ImmutableList.Builder predicateListBuilder = ImmutableList.builder(); + ObjectArrayList predicateList = new ObjectArrayList<>(); for (int i = 0; i < heightStrs.length; i++) { String heightStr = heightStrs[i]; - if (!heightStr.isEmpty()) { - String[] parts = heightStr.split("\\.\\.", 2); - if (parts.length != 0) { - if (parts.length == 2) { - try { - if (parts[1].isEmpty()) { - int min = Integer.parseInt(parts[0]); - predicateListBuilder.add(y -> y >= min); - } else if (parts[0].isEmpty()) { - int max = Integer.parseInt(parts[1]); - predicateListBuilder.add(y -> y <= max); - } else { - int min = Integer.parseInt(parts[0]); - int max = Integer.parseInt(parts[1]); - if (min < max) { - predicateListBuilder.add(y -> y >= min && y <= max); - } else if (min > max) { - predicateListBuilder.add(y -> y >= max && y <= min); - } else { - predicateListBuilder.add(y -> y == min); - } - } - continue; - } catch (NumberFormatException e) { - // + if (heightStr.isEmpty()) { + continue; + } + + String[] parts = heightStr.split("\\.\\.", 2); + if (parts.length == 2) { + try { + if (parts[1].isEmpty()) { + int min = Integer.parseInt(parts[0]); + predicateList.add(y -> y >= min); + } else if (parts[0].isEmpty()) { + int max = Integer.parseInt(parts[1]); + predicateList.add(y -> y <= max); + } else { + int min = Integer.parseInt(parts[0]); + int max = Integer.parseInt(parts[1]); + if (min < max) { + predicateList.add(y -> y >= min && y <= max); + } else if (min > max) { + predicateList.add(y -> y >= max && y <= min); + } else { + predicateList.add(y -> y == min); } - } else if (parts.length == 1) { - String heightStr1 = heightStr.replaceAll("[()]", ""); - if (!heightStr1.isEmpty()) { - int separatorIndex = heightStr1.indexOf('-', heightStr1.charAt(0) == '-' ? 1 : 0); - try { - if (separatorIndex == -1) { - int height = Integer.parseInt(heightStr1); - predicateListBuilder.add(y -> y == height); - } else { - int min = Integer.parseInt(heightStr1.substring(0, separatorIndex)); - int max = Integer.parseInt(heightStr1.substring(separatorIndex + 1)); - if (min < max) { - predicateListBuilder.add(y -> y >= min && y <= max); - } else if (min > max) { - predicateListBuilder.add(y -> y >= max && y <= min); - } else { - predicateListBuilder.add(y -> y == min); - } - } - continue; - } catch (NumberFormatException e) { - // + } + continue; + } catch (NumberFormatException e) { + // + } + } else if (parts.length == 1) { + String heightStr1 = heightStr.replaceAll("[()]", ""); + if (!heightStr1.isEmpty()) { + int separatorIndex = heightStr1.indexOf('-', heightStr1.charAt(0) == '-' ? 1 : 0); + try { + if (separatorIndex == -1) { + int height = Integer.parseInt(heightStr1); + predicateList.add(y -> y == height); + } else { + int min = Integer.parseInt(heightStr1.substring(0, separatorIndex)); + int max = Integer.parseInt(heightStr1.substring(separatorIndex + 1)); + if (min < max) { + predicateList.add(y -> y >= min && y <= max); + } else if (min > max) { + predicateList.add(y -> y >= max && y <= min); + } else { + predicateList.add(y -> y == min); } } + continue; + } catch (NumberFormatException e) { + // } - ContinuityClient.LOGGER.warn("Invalid 'heights' element '" + heightStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); } } + ContinuityClient.LOGGER.warn("Invalid 'heights' element '" + heightStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); } - ImmutableList predicateList = predicateListBuilder.build(); if (!predicateList.isEmpty()) { IntPredicate[] predicateArray = predicateList.toArray(IntPredicate[]::new); heightPredicate = y -> { @@ -474,16 +482,16 @@ protected void parseLegacyHeights() { try { min = Integer.parseInt(minHeightStr.trim()); } catch (NumberFormatException e) { - hasMinHeight = false; ContinuityClient.LOGGER.warn("Invalid 'minHeight' value '" + minHeightStr + "' in file '" + id + "' in pack '" + packName + "'"); + hasMinHeight = false; } } if (hasMaxHeight) { try { max = Integer.parseInt(maxHeightStr.trim()); } catch (NumberFormatException e) { - hasMaxHeight = false; ContinuityClient.LOGGER.warn("Invalid 'maxHeight' value '" + minHeightStr + "' in file '" + id + "' in pack '" + packName + "'"); + hasMaxHeight = false; } } @@ -569,41 +577,45 @@ protected void parseResourceCondition() { for (int i = 0; i < conditionStrs.length; i++) { String conditionStr = conditionStrs[i]; - if (!conditionStr.isEmpty()) { - String[] parts = conditionStr.split("@", 2); - if (parts.length != 0) { - String resourceStr = parts[0]; - Identifier resourceId; - try { - resourceId = new Identifier(resourceStr); - } catch (InvalidIdentifierException e) { - ContinuityClient.LOGGER.warn("Invalid resource '" + resourceStr + "' in 'resourceCondition' element '" + conditionStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); - continue; - } + if (conditionStr.isEmpty()) { + continue; + } - String packStr; - if (parts.length > 1) { - packStr = parts[1]; - } else { - packStr = null; - } + String[] parts = conditionStr.split("@", 2); + if (parts.length != 0) { + String resourceStr = parts[0]; + Identifier resourceId; + try { + resourceId = new Identifier(resourceStr); + } catch (InvalidIdentifierException e) { + ContinuityClient.LOGGER.warn("Invalid resource '" + resourceStr + "' in 'resourceCondition' element '" + conditionStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'", e); + continue; + } - if (packStr == null || packStr.equals("default")) { - Optional optionalResource = resourceManager.getResource(resourceId); - if (optionalResource.isPresent() && optionalResource.get().getPack() != defaultPack) { - valid = false; - break; - } - } else if (packStr.equals("programmer_art")) { - Optional optionalResource = resourceManager.getResource(resourceId); - if (optionalResource.isPresent() && !optionalResource.get().getPack().getName().equals("programmer_art")) { - valid = false; - break; - } - } else { - ContinuityClient.LOGGER.warn("Unknown pack '" + packStr + "' in 'resourceCondition' element '" + conditionStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); + String packStr; + if (parts.length > 1) { + packStr = parts[1]; + } else { + packStr = null; + } + + if (packStr == null || packStr.equals("default")) { + Optional optionalResource = resourceManager.getResource(resourceId); + if (optionalResource.isPresent() && optionalResource.get().getPack() != defaultPack) { + valid = false; + break; } + } else if (packStr.equals("programmer_art")) { + Optional optionalResource = resourceManager.getResource(resourceId); + if (optionalResource.isPresent() && !optionalResource.get().getPack().getName().equals("programmer_art")) { + valid = false; + break; + } + } else { + ContinuityClient.LOGGER.warn("Unknown pack '" + packStr + "' in 'resourceCondition' element '" + conditionStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); } + } else { + ContinuityClient.LOGGER.warn("Invalid 'resourceCondition' element '" + conditionStr + "' at index " + i + " in file '" + id + "' in pack '" + packName + "'"); } } } @@ -668,10 +680,12 @@ public String getMethod() { return method; } + @Nullable public Set getMatchTilesSet() { return matchTilesSet; } + @Nullable public Predicate getMatchBlocksPredicate() { return matchBlocksPredicate; } @@ -680,18 +694,22 @@ public int getTileAmount() { return tiles.size(); } + @Nullable public EnumSet getFaces() { return faces; } + @Nullable public Predicate getBiomePredicate() { return biomePredicate; } + @Nullable public IntPredicate getHeightPredicate() { return heightPredicate; } + @Nullable public Predicate getBlockEntityNamePredicate() { return blockEntityNamePredicate; } diff --git a/src/main/java/me/pepperbell/continuity/client/properties/CompactConnectingCTMProperties.java b/src/main/java/me/pepperbell/continuity/client/properties/CompactConnectingCTMProperties.java index b1b052b..9763b50 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/CompactConnectingCTMProperties.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/CompactConnectingCTMProperties.java @@ -2,6 +2,8 @@ import java.util.Properties; +import org.jetbrains.annotations.Nullable; + import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; import it.unimi.dsi.fastutil.ints.Int2IntMap; import me.pepperbell.continuity.client.ContinuityClient; @@ -10,6 +12,7 @@ import net.minecraft.util.Identifier; public class CompactConnectingCTMProperties extends StandardConnectingCTMProperties { + @Nullable protected Int2IntMap tileReplacementMap; public CompactConnectingCTMProperties(Properties properties, Identifier id, ResourcePack pack, int packPriority, ResourceManager resourceManager, String method) { @@ -58,6 +61,7 @@ protected void parseTileReplacements() { } } + @Nullable public Int2IntMap getTileReplacementMap() { return tileReplacementMap; } diff --git a/src/main/java/me/pepperbell/continuity/client/properties/PropertiesParsingHelper.java b/src/main/java/me/pepperbell/continuity/client/properties/PropertiesParsingHelper.java index 67b20d0..aba1eed 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/PropertiesParsingHelper.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/PropertiesParsingHelper.java @@ -1,19 +1,25 @@ package me.pepperbell.continuity.client.properties; +import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Properties; +import java.util.Set; import java.util.function.IntFunction; import java.util.function.Predicate; import org.apache.commons.io.FilenameUtils; import org.jetbrains.annotations.Nullable; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import me.pepperbell.continuity.client.ContinuityClient; import me.pepperbell.continuity.client.processor.Symmetry; import me.pepperbell.continuity.client.resource.ResourceRedirectHandler; @@ -29,7 +35,7 @@ public final class PropertiesParsingHelper { public static final Predicate EMPTY_BLOCK_STATE_PREDICATE = state -> false; @Nullable - public static ImmutableSet parseMatchTiles(Properties properties, String propertyKey, Identifier fileLocation, String packName, @Nullable ResourceRedirectHandler redirectHandler) { + public static Set parseMatchTiles(Properties properties, String propertyKey, Identifier fileLocation, String packName, @Nullable ResourceRedirectHandler redirectHandler) { String matchTilesStr = properties.getProperty(propertyKey); if (matchTilesStr == null) { return null; @@ -38,71 +44,74 @@ public static ImmutableSet parseMatchTiles(Properties properties, St String[] matchTileStrs = matchTilesStr.trim().split(" "); if (matchTileStrs.length != 0) { String basePath = FilenameUtils.getPath(fileLocation.getPath()); - ImmutableSet.Builder setBuilder = ImmutableSet.builder(); + ObjectOpenHashSet set = new ObjectOpenHashSet<>(); for (int i = 0; i < matchTileStrs.length; i++) { String matchTileStr = matchTileStrs[i]; - if (!matchTileStr.isEmpty()) { - String[] parts = matchTileStr.split(":", 2); - if (parts.length != 0) { - String namespace; - String path; - if (parts.length > 1) { - namespace = parts[0]; - path = parts[1]; - } else { - namespace = null; - path = parts[0]; - } + if (matchTileStr.isEmpty()) { + continue; + } - if (path.endsWith(".png")) { - path = path.substring(0, path.length() - 4); - } + String[] parts = matchTileStr.split(":", 2); + if (parts.length != 0) { + String namespace; + String path; + if (parts.length > 1) { + namespace = parts[0]; + path = parts[1]; + } else { + namespace = null; + path = parts[0]; + } - if (namespace == null) { - if (path.startsWith("assets/minecraft/")) { - path = path.substring(17); - } else if (path.startsWith("./")) { - path = basePath + path.substring(2); - } else if (path.startsWith("~/")) { - path = "optifine/" + path.substring(2); - } else if (path.startsWith("/")) { - path = "optifine/" + path.substring(1); - } - } + if (path.endsWith(".png")) { + path = path.substring(0, path.length() - 4); + } - if (path.startsWith("textures/")) { - path = path.substring(9); - } else if (path.startsWith("optifine/")) { - if (redirectHandler == null) { - continue; - } - path = redirectHandler.getSourceSpritePath(path + ".png"); - if (namespace == null) { - namespace = fileLocation.getNamespace(); - } - } else if (!path.contains("/")) { - path = "block/" + path; + if (namespace == null) { + if (path.startsWith("assets/minecraft/")) { + path = path.substring(17); + } else if (path.startsWith("./")) { + path = basePath + path.substring(2); + } else if (path.startsWith("~/")) { + path = "optifine/" + path.substring(2); + } else if (path.startsWith("/")) { + path = "optifine/" + path.substring(1); } + } + if (path.startsWith("textures/")) { + path = path.substring(9); + } else if (path.startsWith("optifine/")) { + if (redirectHandler == null) { + continue; + } + path = redirectHandler.getSourceSpritePath(path + ".png"); if (namespace == null) { - namespace = Identifier.DEFAULT_NAMESPACE; + namespace = fileLocation.getNamespace(); } + } else if (!path.contains("/")) { + path = "block/" + path; + } - try { - setBuilder.add(new Identifier(namespace, path)); - continue; - } catch (InvalidIdentifierException e) { - // - } + if (namespace == null) { + namespace = Identifier.DEFAULT_NAMESPACE; } + + try { + set.add(new Identifier(namespace, path)); + } catch (InvalidIdentifierException e) { + ContinuityClient.LOGGER.warn("Invalid '" + propertyKey + "' element '" + matchTileStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'", e); + } + } else { ContinuityClient.LOGGER.warn("Invalid '" + propertyKey + "' element '" + matchTileStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'"); } } - return setBuilder.build(); + set.trim(); + return set; } - return ImmutableSet.of(); + return Collections.emptySet(); } @Nullable @@ -114,33 +123,38 @@ public static Predicate parseBlockStates(Properties properties, Stri String[] blockStateStrs = blockStatesStr.trim().split(" "); if (blockStateStrs.length != 0) { - ImmutableList.Builder> predicateListBuilder = ImmutableList.builder(); + ReferenceOpenHashSet blockSet = new ReferenceOpenHashSet<>(); + Reference2ObjectOpenHashMap, ObjectOpenHashSet>>> propertyMaps = new Reference2ObjectOpenHashMap<>(); Block: for (int i = 0; i < blockStateStrs.length; i++) { String blockStateStr = blockStateStrs[i].trim(); - if (!blockStateStr.isEmpty()) { - String[] parts = blockStateStr.split(":"); - if (parts.length != 0) { - Identifier blockId; - int startIndex; - try { - if (parts.length == 1 || parts[1].contains("=")) { - blockId = new Identifier(parts[0]); - startIndex = 1; - } else { - blockId = new Identifier(parts[0], parts[1]); - startIndex = 2; - } - } catch (InvalidIdentifierException e) { - ContinuityClient.LOGGER.warn("Invalid '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'", e); - continue; + if (blockStateStr.isEmpty()) { + continue; + } + + String[] parts = blockStateStr.split(":"); + if (parts.length != 0) { + Identifier blockId; + int startIndex; + try { + if (parts.length == 1 || parts[1].contains("=")) { + blockId = new Identifier(parts[0]); + startIndex = 1; + } else { + blockId = new Identifier(parts[0], parts[1]); + startIndex = 2; } + } catch (InvalidIdentifierException e) { + ContinuityClient.LOGGER.warn("Invalid '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'", e); + continue; + } - Block block = Registries.BLOCK.get(blockId); - if (block != Blocks.AIR) { + Block block = Registries.BLOCK.get(blockId); + if (block != Blocks.AIR) { + if (!blockSet.contains(block)) { if (parts.length > startIndex) { - ImmutableMap.Builder, Comparable[]> propertyMapBuilder = ImmutableMap.builder(); + Object2ObjectOpenHashMap, ObjectOpenHashSet>> propertyMap = new Object2ObjectOpenHashMap<>(); for (int j = startIndex; j < parts.length; j++) { String part = parts[j]; @@ -153,21 +167,20 @@ public static Predicate parseBlockStates(Properties properties, Stri String propertyValuesStr = propertyParts[1]; String[] propertyValueStrs = propertyValuesStr.split(","); if (propertyValueStrs.length != 0) { - ImmutableList.Builder> valueListBuilder = ImmutableList.builder(); + ObjectOpenHashSet> valueSet = propertyMap.computeIfAbsent(property, p -> new ObjectOpenHashSet<>(Hash.DEFAULT_INITIAL_SIZE, Hash.VERY_FAST_LOAD_FACTOR)); for (String propertyValueStr : propertyValueStrs) { - Optional> optional = property.parse(propertyValueStr); - if (optional.isPresent()) { - valueListBuilder.add(optional.get()); + Optional> optionalValue = property.parse(propertyValueStr); + if (optionalValue.isPresent()) { + valueSet.add(optionalValue.get()); } else { ContinuityClient.LOGGER.warn("Invalid block property value '" + propertyValueStr + "' for property '" + propertyName + "' for block '" + blockId + "' in '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'"); continue Block; } } - - ImmutableList> valueList = valueListBuilder.build(); - Comparable[] valueArray = valueList.toArray(Comparable[]::new); - propertyMapBuilder.put(property, valueArray); + } else { + ContinuityClient.LOGGER.warn("Invalid block property definition for block '" + blockId + "' in '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'"); + continue Block; } } else { ContinuityClient.LOGGER.warn("Unknown block property '" + propertyName + "' for block '" + blockId + "' in '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'"); @@ -180,48 +193,73 @@ public static Predicate parseBlockStates(Properties properties, Stri } } - ImmutableMap, Comparable[]> propertyMap = propertyMapBuilder.build(); if (!propertyMap.isEmpty()) { - Map.Entry, Comparable[]>[] propertyMapEntryArray = propertyMap.entrySet().toArray((IntFunction, Comparable[]>[]>) Map.Entry[]::new); - predicateListBuilder.add(state -> { - if (state.getBlock() == block) { - Outer: - for (Map.Entry, Comparable[]> entry : propertyMapEntryArray) { - Comparable targetValue = state.get(entry.getKey()); - Comparable[] valueArray = entry.getValue(); - for (Comparable value : valueArray) { - if (targetValue == value) { - continue Outer; - } - } - return false; + Object2ObjectOpenHashMap, ObjectOpenHashSet>> existingPropertyMap = propertyMaps.get(block); + if (existingPropertyMap == null) { + propertyMaps.put(block, propertyMap); + } else { + propertyMap.forEach((property, valueSet) -> { + ObjectOpenHashSet> existingValueSet = existingPropertyMap.get(property); + if (existingValueSet == null) { + existingPropertyMap.put(property, valueSet); + } else { + existingValueSet.addAll(valueSet); } - return true; - } - return false; - }); + }); + } } } else { - predicateListBuilder.add(state -> state.getBlock() == block); + blockSet.add(block); + propertyMaps.remove(block); } - } else { - ContinuityClient.LOGGER.warn("Unknown block '" + blockId + "' in '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'"); } + } else { + ContinuityClient.LOGGER.warn("Unknown block '" + blockId + "' in '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'"); } + } else { + ContinuityClient.LOGGER.warn("Invalid '" + propertyKey + "' element '" + blockStateStr + "' at index " + i + " in file '" + fileLocation + "' in pack '" + packName + "'"); } } - ImmutableList> predicateList = predicateListBuilder.build(); - if (!predicateList.isEmpty()) { - Predicate[] predicateArray = predicateList.toArray((IntFunction[]>) Predicate[]::new); - return state -> { - for (Predicate predicate : predicateArray) { - if (predicate.test(state)) { - return true; - } + if (!blockSet.isEmpty() || !propertyMaps.isEmpty()) { + if (propertyMaps.isEmpty()) { + if (blockSet.size() == 1) { + Block block = blockSet.toArray(Block[]::new)[0]; + return state -> state.getBlock() == block; + } else { + blockSet.trim(); + return state -> blockSet.contains(state.getBlock()); } - return false; - }; + } else { + Reference2ReferenceOpenHashMap> predicateMap = new Reference2ReferenceOpenHashMap<>(); + blockSet.forEach(block -> { + predicateMap.put(block, state -> true); + }); + propertyMaps.forEach((block, propertyMap) -> { + Map.Entry, ObjectOpenHashSet>>[] entryArray = propertyMap.entrySet().toArray((IntFunction, ObjectOpenHashSet>>[]>) Map.Entry[]::new); + for (Map.Entry, ObjectOpenHashSet>> entry : entryArray) { + entry.getValue().trim(); + } + + predicateMap.put(block, state -> { + ImmutableMap, Comparable> targetValueMap = state.getEntries(); + for (Map.Entry, ObjectOpenHashSet>> entry : entryArray) { + Comparable targetValue = targetValueMap.get(entry.getKey()); + if (targetValue != null) { + if (!entry.getValue().contains(targetValue)) { + return false; + } + } + } + return true; + }); + }); + + return state -> { + Predicate predicate = predicateMap.get(state.getBlock()); + return predicate != null && predicate.test(state); + }; + } } } return EMPTY_BLOCK_STATE_PREDICATE; diff --git a/src/main/java/me/pepperbell/continuity/client/properties/RandomCTMProperties.java b/src/main/java/me/pepperbell/continuity/client/properties/RandomCTMProperties.java index ae64a2a..71bfd0c 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/RandomCTMProperties.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/RandomCTMProperties.java @@ -42,30 +42,32 @@ protected void parseWeights() { for (int i = 0; i < weightStrs.length; i++) { String weightStr = weightStrs[i]; - if (!weightStr.isEmpty()) { - String[] parts = weightStr.split("-"); - try { - if (parts.length == 2) { - int min = Integer.parseInt(parts[0]); - int max = Integer.parseInt(parts[1]); - if (min > 0 && max > 0 && max >= min) { - for (int weight = min; weight <= max; weight++) { - weights.add(weight); - } - continue; - } - } else if (parts.length == 1) { - int weight = Integer.parseInt(parts[0]); - if (weight > 0) { + if (weightStr.isEmpty()) { + continue; + } + + String[] parts = weightStr.split("-", 2); + try { + if (parts.length == 2) { + int min = Integer.parseInt(parts[0]); + int max = Integer.parseInt(parts[1]); + if (min > 0 && max > 0 && max >= min) { + for (int weight = min; weight <= max; weight++) { weights.add(weight); - continue; } + continue; + } + } else if (parts.length == 1) { + int weight = Integer.parseInt(parts[0]); + if (weight > 0) { + weights.add(weight); + continue; } - } catch (NumberFormatException e) { - // } - ContinuityClient.LOGGER.warn("Invalid 'weights' element '" + weightStr + "' at index '" + i + "' in file '" + id + "' in pack '" + packName + "'"); + } catch (NumberFormatException e) { + // } + ContinuityClient.LOGGER.warn("Invalid 'weights' element '" + weightStr + "' at index '" + i + "' in file '" + id + "' in pack '" + packName + "'"); } if (!weights.isEmpty()) { diff --git a/src/main/java/me/pepperbell/continuity/client/properties/overlay/OverlayPropertiesSection.java b/src/main/java/me/pepperbell/continuity/client/properties/overlay/OverlayPropertiesSection.java index 8377cb2..1bce26d 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/overlay/OverlayPropertiesSection.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/overlay/OverlayPropertiesSection.java @@ -3,6 +3,8 @@ import java.util.Locale; import java.util.Properties; +import org.jetbrains.annotations.Nullable; + import me.pepperbell.continuity.client.ContinuityClient; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; import net.minecraft.block.Block; @@ -18,6 +20,7 @@ public class OverlayPropertiesSection { protected String packName; protected int tintIndex = -1; + @Nullable protected BlockState tintBlock; protected BlendMode layer = BlendMode.CUTOUT_MIPPED; @@ -77,6 +80,8 @@ protected void parseTintBlock() { } else { ContinuityClient.LOGGER.warn("Unknown block '" + blockId + "' in 'tintBlock' value '" + tintBlockStr + "' in file '" + id + "' in pack '" + packName + "'"); } + } else { + ContinuityClient.LOGGER.warn("Invalid 'tintBlock' value '" + tintBlockStr + "' in file '" + id + "' in pack '" + packName + "'"); } } @@ -99,6 +104,7 @@ public int getTintIndex() { return tintIndex; } + @Nullable public BlockState getTintBlock() { return tintBlock; } diff --git a/src/main/java/me/pepperbell/continuity/client/properties/overlay/StandardOverlayCTMProperties.java b/src/main/java/me/pepperbell/continuity/client/properties/overlay/StandardOverlayCTMProperties.java index e3c3afe..8b24ac4 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/overlay/StandardOverlayCTMProperties.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/overlay/StandardOverlayCTMProperties.java @@ -4,6 +4,8 @@ import java.util.Set; import java.util.function.Predicate; +import org.jetbrains.annotations.Nullable; + import me.pepperbell.continuity.client.properties.ConnectingCTMProperties; import me.pepperbell.continuity.client.properties.PropertiesParsingHelper; import me.pepperbell.continuity.client.resource.ResourceRedirectHandler; @@ -14,7 +16,9 @@ public class StandardOverlayCTMProperties extends ConnectingCTMProperties implements OverlayPropertiesSection.Provider { protected OverlayPropertiesSection overlaySection; + @Nullable protected Set connectTilesSet; + @Nullable protected Predicate connectBlocksPredicate; public StandardOverlayCTMProperties(Properties properties, Identifier id, ResourcePack pack, int packPriority, ResourceManager resourceManager, String method) { @@ -43,10 +47,12 @@ protected void parseConnectBlocks() { connectBlocksPredicate = PropertiesParsingHelper.parseBlockStates(properties, "connectBlocks", id, packName); } + @Nullable public Set getConnectTilesSet() { return connectTilesSet; } + @Nullable public Predicate getConnectBlocksPredicate() { return connectBlocksPredicate; } diff --git a/src/main/java/me/pepperbell/continuity/client/resource/BakedModelManagerReloadExtension.java b/src/main/java/me/pepperbell/continuity/client/resource/BakedModelManagerReloadExtension.java index 75f744d..257db20 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/BakedModelManagerReloadExtension.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/BakedModelManagerReloadExtension.java @@ -31,7 +31,7 @@ public BakedModelManagerReloadExtension(ResourceManager resourceManager, Executo } public void setContext() { - SpriteLoaderLoadContext.THREAD_LOCAL.set(new SpriteLoaderInitContextImpl(ctmLoadingResultFuture.thenApply(CTMPropertiesLoader.LoadingResult::textureDependencies), wrapEmissiveModels)); + SpriteLoaderLoadContext.THREAD_LOCAL.set(new SpriteLoaderInitContextImpl(ctmLoadingResultFuture.thenApply(CTMPropertiesLoader.LoadingResult::getTextureDependencies), wrapEmissiveModels)); } public void clearContext() { @@ -41,7 +41,7 @@ public void clearContext() { public void beforeBaking(Map preparations, ModelLoader modelLoader) { CTMPropertiesLoader.LoadingResult result = ctmLoadingResultFuture.join(); - List processorHolders = CTMPropertiesLoader.createProcessorHolders(result.containers(), spriteId -> { + List processorHolders = result.createProcessorHolders(spriteId -> { SpriteAtlasManager.AtlasPreparation preparation = preparations.get(spriteId.getAtlasId()); Sprite sprite = preparation.getSprite(spriteId.getTextureId()); if (sprite != null) { diff --git a/src/main/java/me/pepperbell/continuity/client/resource/CTMPropertiesLoader.java b/src/main/java/me/pepperbell/continuity/client/resource/CTMPropertiesLoader.java index 8354023..4178c90 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/CTMPropertiesLoader.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/CTMPropertiesLoader.java @@ -44,9 +44,7 @@ public static LoadingResult loadAllWithState(ResourceManager resourceManager) { } public static LoadingResult loadAll(ResourceManager resourceManager) { - List> containers = new ObjectArrayList<>(); - Map> textureDependencies = new Object2ObjectOpenHashMap<>(); - LoadingResult result = new LoadingResult(containers, textureDependencies); + LoadingData loadingData = new LoadingData(); int packPriority = 0; Iterator iterator = resourceManager.streamResourcePacks().iterator(); @@ -54,23 +52,23 @@ public static LoadingResult loadAll(ResourceManager resourceManager) { invalidIdentifierState.enable(); while (iterator.hasNext()) { ResourcePack pack = iterator.next(); - loadAll(pack, packPriority, resourceManager, result); + loadAll(pack, packPriority, resourceManager, loadingData); packPriority++; } invalidIdentifierState.disable(); - containers.sort(Comparator.reverseOrder()); + loadingData.containers.sort(Comparator.reverseOrder()); - return result; + return new LoadingResult(loadingData.containers, loadingData.textureDependencies); } - private static void loadAll(ResourcePack pack, int packPriority, ResourceManager resourceManager, LoadingResult result) { + private static void loadAll(ResourcePack pack, int packPriority, ResourceManager resourceManager, LoadingData loadingData) { for (String namespace : pack.getNamespaces(ResourceType.CLIENT_RESOURCES)) { pack.findResources(ResourceType.CLIENT_RESOURCES, namespace, "optifine/ctm", (id, inputSupplier) -> { if (id.getPath().endsWith(".properties")) { try (InputStream stream = inputSupplier.get()) { Properties properties = new Properties(); properties.load(stream); - load(properties, id, pack, packPriority, resourceManager, result); + load(properties, id, pack, packPriority, resourceManager, loadingData); } catch (Exception e) { ContinuityClient.LOGGER.error("Failed to load CTM properties from file '" + id + "' in pack '" + pack.getName() + "'", e); } @@ -79,37 +77,34 @@ private static void loadAll(ResourcePack pack, int packPriority, ResourceManager } } - private static void load(Properties properties, Identifier id, ResourcePack pack, int packPriority, ResourceManager resourceManager, LoadingResult result) { + private static void load(Properties properties, Identifier id, ResourcePack pack, int packPriority, ResourceManager resourceManager, LoadingData loadingData) { String method = properties.getProperty("method", "ctm").trim(); CTMLoader loader = CTMLoaderRegistry.get().getLoader(method); if (loader != null) { - load(loader, properties, id, pack, packPriority, resourceManager, method, result); + load(loader, properties, id, pack, packPriority, resourceManager, method, loadingData); } else { ContinuityClient.LOGGER.error("Unknown 'method' value '" + method + "' in file '" + id + "' in pack '" + pack.getName() + "'"); } } - private static void load(CTMLoader loader, Properties properties, Identifier id, ResourcePack pack, int packPriority, ResourceManager resourceManager, String method, LoadingResult result) { + private static void load(CTMLoader loader, Properties properties, Identifier id, ResourcePack pack, int packPriority, ResourceManager resourceManager, String method, LoadingData loadingData) { T ctmProperties = loader.getPropertiesFactory().createProperties(properties, id, pack, packPriority, resourceManager, method); if (ctmProperties != null) { LoadingContainer container = new LoadingContainer<>(loader, ctmProperties); - result.containers().add(container); + loadingData.containers.add(container); for (SpriteIdentifier spriteId : ctmProperties.getTextureDependencies()) { - Set atlasTextureDependencies = result.textureDependencies().computeIfAbsent(spriteId.getAtlasId(), id1 -> new ObjectOpenHashSet<>()); + Set atlasTextureDependencies = loadingData.textureDependencies.computeIfAbsent(spriteId.getAtlasId(), id1 -> new ObjectOpenHashSet<>()); atlasTextureDependencies.add(spriteId.getTextureId()); } } } - public static List createProcessorHolders(List> containers, Function textureGetter) { - List processorHolders = new ObjectArrayList<>(); - for (LoadingContainer container : containers) { - processorHolders.add(container.toProcessorHolder(textureGetter)); - } - return processorHolders; + private static class LoadingData { + public final List> containers = new ObjectArrayList<>(); + public final Map> textureDependencies = new Object2ObjectOpenHashMap<>(); } - public record LoadingContainer(CTMLoader loader, T properties) implements Comparable> { + private record LoadingContainer(CTMLoader loader, T properties) implements Comparable> { public QuadProcessors.ProcessorHolder toProcessorHolder(Function textureGetter) { QuadProcessor processor = loader.getProcessorFactory().createProcessor(properties, textureGetter); CachingPredicates predicates = loader.getPredicatesFactory().createPredicates(properties, textureGetter); @@ -122,6 +117,25 @@ public int compareTo(@NotNull LoadingContainer o) { } } - public record LoadingResult(List> containers, Map> textureDependencies) { + public static class LoadingResult { + private final List> containers; + private final Map> textureDependencies; + + private LoadingResult(List> containers, Map> textureDependencies) { + this.containers = containers; + this.textureDependencies = textureDependencies; + } + + public List createProcessorHolders(Function textureGetter) { + List processorHolders = new ObjectArrayList<>(); + for (LoadingContainer container : containers) { + processorHolders.add(container.toProcessorHolder(textureGetter)); + } + return processorHolders; + } + + public Map> getTextureDependencies() { + return textureDependencies; + } } } diff --git a/src/main/java/me/pepperbell/continuity/client/resource/CustomBlockLayers.java b/src/main/java/me/pepperbell/continuity/client/resource/CustomBlockLayers.java index 8fafdba..0b16bf5 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/CustomBlockLayers.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/CustomBlockLayers.java @@ -7,7 +7,6 @@ import java.util.Properties; import java.util.function.Predicate; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import me.pepperbell.continuity.client.ContinuityClient; @@ -32,8 +31,14 @@ public final class CustomBlockLayers { @SuppressWarnings("unchecked") private static final Predicate[] LAYER_PREDICATES = new Predicate[BlockLayer.VALUES.length]; + private static boolean empty; + private static boolean disableSolidCheck; + public static boolean isEmpty() { + return empty; + } + @Nullable public static RenderLayer getLayer(BlockState state) { if (!disableSolidCheck) { @@ -54,7 +59,9 @@ public static RenderLayer getLayer(BlockState state) { } private static void reload(ResourceManager manager) { + empty = true; System.arraycopy(EMPTY_LAYER_PREDICATES, 0, LAYER_PREDICATES, 0, EMPTY_LAYER_PREDICATES.length); + disableSolidCheck = false; Optional optionalResource = manager.getResource(LOCATION); if (optionalResource.isPresent()) { @@ -64,7 +71,7 @@ private static void reload(ResourceManager manager) { properties.load(inputStream); reload(properties, LOCATION, resource.getResourcePackName()); } catch (IOException e) { - ContinuityClient.LOGGER.error("Failed to load custom block layers from file '" + LOCATION + "'", e); + ContinuityClient.LOGGER.error("Failed to load custom block layers from file '" + LOCATION + "' from pack '" + resource.getResourcePackName() + "'", e); } } } @@ -73,8 +80,9 @@ private static void reload(Properties properties, Identifier fileLocation, Strin for (BlockLayer blockLayer : BlockLayer.VALUES) { String propertyKey = "layer." + blockLayer.getKey(); Predicate predicate = PropertiesParsingHelper.parseBlockStates(properties, propertyKey, fileLocation, packName); - if (predicate != PropertiesParsingHelper.EMPTY_BLOCK_STATE_PREDICATE) { + if (predicate != null && predicate != PropertiesParsingHelper.EMPTY_BLOCK_STATE_PREDICATE) { LAYER_PREDICATES[blockLayer.ordinal()] = predicate; + empty = false; } } @@ -88,7 +96,6 @@ public static class ReloadListener implements SimpleSynchronousResourceReloadLis public static final Identifier ID = ContinuityClient.asId("custom_block_layers"); private static final ReloadListener INSTANCE = new ReloadListener(); - @ApiStatus.Internal public static void init() { ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(INSTANCE); } diff --git a/src/main/java/me/pepperbell/continuity/client/resource/EmissiveSuffixLoader.java b/src/main/java/me/pepperbell/continuity/client/resource/EmissiveSuffixLoader.java index 29d5adc..afba957 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/EmissiveSuffixLoader.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/EmissiveSuffixLoader.java @@ -5,7 +5,6 @@ import java.util.Optional; import java.util.Properties; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import me.pepperbell.continuity.client.ContinuityClient; @@ -23,7 +22,6 @@ public static String getEmissiveSuffix() { return emissiveSuffix; } - @ApiStatus.Internal public static void load(ResourceManager manager) { emissiveSuffix = null; diff --git a/src/main/java/me/pepperbell/continuity/client/resource/ModelWrappingHandler.java b/src/main/java/me/pepperbell/continuity/client/resource/ModelWrappingHandler.java index d6c0f22..95828e9 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/ModelWrappingHandler.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/ModelWrappingHandler.java @@ -1,5 +1,7 @@ package me.pepperbell.continuity.client.resource; +import org.jetbrains.annotations.Nullable; + import com.google.common.collect.ImmutableMap; import me.pepperbell.continuity.client.model.CTMBakedModel; @@ -29,7 +31,7 @@ private static ImmutableMap createBlockStateModelId return builder.build(); } - public static BakedModel wrap(BakedModel model, Identifier modelId, boolean wrapCTM, boolean wrapEmissive) { + public static BakedModel wrap(@Nullable BakedModel model, Identifier modelId, boolean wrapCTM, boolean wrapEmissive) { if (model != null && !model.isBuiltin() && !modelId.equals(ModelLoader.MISSING_ID)) { if (wrapCTM) { if (modelId instanceof ModelIdentifier) { diff --git a/src/main/java/me/pepperbell/continuity/client/util/RenderUtil.java b/src/main/java/me/pepperbell/continuity/client/util/RenderUtil.java index 18c4734..684c04e 100644 --- a/src/main/java/me/pepperbell/continuity/client/util/RenderUtil.java +++ b/src/main/java/me/pepperbell/continuity/client/util/RenderUtil.java @@ -4,7 +4,7 @@ import java.util.Collection; import java.util.List; -import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import io.vram.frex.api.material.MaterialConstants; import io.vram.frex.fabric.compat.FabricQuadView; @@ -82,7 +82,7 @@ private static BlendModeGetter createBlendModeGetter() { return quad -> BlendMode.DEFAULT; } - public static int getTintColor(BlockState state, BlockRenderView blockView, BlockPos pos, int tintIndex) { + public static int getTintColor(@Nullable BlockState state, BlockRenderView blockView, BlockPos pos, int tintIndex) { if (state == null || tintIndex == -1) { return -1; } @@ -110,7 +110,6 @@ public static class ReloadListener implements SimpleSynchronousResourceReloadLis public static final List DEPENDENCIES = List.of(ResourceReloadListenerKeys.MODELS); private static final ReloadListener INSTANCE = new ReloadListener(); - @ApiStatus.Internal public static void init() { ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(INSTANCE); } diff --git a/src/main/java/me/pepperbell/continuity/client/util/SpriteCalculator.java b/src/main/java/me/pepperbell/continuity/client/util/SpriteCalculator.java index 96c8386..697be1a 100644 --- a/src/main/java/me/pepperbell/continuity/client/util/SpriteCalculator.java +++ b/src/main/java/me/pepperbell/continuity/client/util/SpriteCalculator.java @@ -6,9 +6,7 @@ import java.util.concurrent.locks.StampedLock; import java.util.function.Supplier; -import org.jetbrains.annotations.ApiStatus; - -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.block.BlockModels; @@ -55,7 +53,6 @@ public static Sprite calculateSprite(BlockState state, Direction face, Supplier< return model.getParticleSprite(); } - @ApiStatus.Internal public static void clearCache() { for (SpriteCache cache : SPRITE_CACHES.values()) { cache.clear(); @@ -64,7 +61,7 @@ public static void clearCache() { private static class SpriteCache { private final Direction face; - private final Map sprites = new Object2ObjectOpenHashMap<>(); + private final Map sprites = new Reference2ReferenceOpenHashMap<>(); private final Supplier randomSupplier = new Supplier<>() { private final Random random = Random.create(); diff --git a/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolder.java b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolder.java index 583ef5f..7e3bd3e 100644 --- a/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolder.java +++ b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolder.java @@ -9,11 +9,11 @@ import net.minecraft.util.Identifier; import net.minecraft.world.biome.Biome; -public class BiomeHolder { - protected Identifier id; - protected Biome biome; +public final class BiomeHolder { + private final Identifier id; + private Biome biome; - protected BiomeHolder(Identifier id) { + BiomeHolder(Identifier id) { this.id = id; } @@ -26,15 +26,28 @@ public Biome getBiome() { return biome; } - public void refresh(Registry biomeRegistry, Map compressedIdMap) { - Identifier realId = compressedIdMap.get(id); - if (realId == null) { - realId = id; + void refresh(Registry biomeRegistry, Map compactIdMap) { + Identifier id = compactIdMap.get(this.id); + if (id == null) { + id = this.id; } - if (biomeRegistry.containsId(realId)) { - biome = biomeRegistry.get(realId); + if (biomeRegistry.containsId(id)) { + biome = biomeRegistry.get(id); } else { - ContinuityClient.LOGGER.warn("Unknown biome '" + id + "'"); + ContinuityClient.LOGGER.warn("Unknown biome '" + this.id + "'"); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BiomeHolder that = (BiomeHolder) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } } diff --git a/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolderManager.java b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolderManager.java index 3033792..c80247d 100644 --- a/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolderManager.java +++ b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeHolderManager.java @@ -1,10 +1,10 @@ package me.pepperbell.continuity.client.util.biome; import java.util.Map; - -import org.jetbrains.annotations.ApiStatus; +import java.util.Set; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.minecraft.registry.DynamicRegistryManager; import net.minecraft.registry.Registry; @@ -14,18 +14,18 @@ public final class BiomeHolderManager { private static final Map HOLDER_CACHE = new Object2ObjectOpenHashMap<>(); + private static final Set REFRESH_CALLBACKS = new ReferenceOpenHashSet<>(); + private static DynamicRegistryManager registryManager; public static BiomeHolder getOrCreateHolder(Identifier id) { - BiomeHolder holder = HOLDER_CACHE.get(id); - if (holder == null) { - holder = new BiomeHolder(id); - HOLDER_CACHE.put(id, holder); - } - return holder; + return HOLDER_CACHE.computeIfAbsent(id, BiomeHolder::new); + } + + public static void addRefreshCallback(Runnable callback) { + REFRESH_CALLBACKS.add(callback); } - @ApiStatus.Internal public static void init() { ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> { registryManager = handler.getRegistryManager(); @@ -38,26 +38,30 @@ public static void refreshHolders() { return; } - Map compressedIdMap = new Object2ObjectOpenHashMap<>(); + Map compactIdMap = new Object2ObjectOpenHashMap<>(); Registry biomeRegistry = registryManager.get(RegistryKeys.BIOME); for (Identifier id : biomeRegistry.getIds()) { String path = id.getPath(); - String compressedPath = path.replace("_", ""); - if (!path.equals(compressedPath)) { - Identifier compressedId = new Identifier(id.getNamespace(), compressedPath); - if (!biomeRegistry.containsId(compressedId)) { - compressedIdMap.put(compressedId, id); + String compactPath = path.replace("_", ""); + if (!path.equals(compactPath)) { + Identifier compactId = new Identifier(id.getNamespace(), compactPath); + if (!biomeRegistry.containsId(compactId)) { + compactIdMap.put(compactId, id); } } } for (BiomeHolder holder : HOLDER_CACHE.values()) { - holder.refresh(biomeRegistry, compressedIdMap); + holder.refresh(biomeRegistry, compactIdMap); + } + + for (Runnable callback : REFRESH_CALLBACKS) { + callback.run(); } } - @ApiStatus.Internal public static void clearCache() { HOLDER_CACHE.clear(); + REFRESH_CALLBACKS.clear(); } } diff --git a/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeRetriever.java b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeRetriever.java index ff645ed..e8dfd4f 100644 --- a/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeRetriever.java +++ b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeRetriever.java @@ -1,7 +1,6 @@ package me.pepperbell.continuity.client.util.biome; import org.apache.commons.lang3.ArrayUtils; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import grondag.canvas.terrain.region.input.InputRegion; @@ -53,7 +52,6 @@ public static Biome getBiome(BlockRenderView blockView, BlockPos pos) { return PROVIDER.getBiome(blockView, pos); } - @ApiStatus.Internal public static void init() { } diff --git a/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeSetPredicate.java b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeSetPredicate.java new file mode 100644 index 0000000..a79e73f --- /dev/null +++ b/src/main/java/me/pepperbell/continuity/client/util/biome/BiomeSetPredicate.java @@ -0,0 +1,35 @@ +package me.pepperbell.continuity.client.util.biome; + +import java.util.Collections; +import java.util.Set; +import java.util.function.Predicate; + +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.world.biome.Biome; + +public class BiomeSetPredicate implements Predicate { + private final Set holders; + private Set biomes = Collections.emptySet(); + + public BiomeSetPredicate(Set holders) { + this.holders = holders; + BiomeHolderManager.addRefreshCallback(this::refresh); + } + + @Override + public boolean test(Biome biome) { + return biomes.contains(biome); + } + + private void refresh() { + Set biomes = new ObjectOpenHashSet<>(Hash.DEFAULT_INITIAL_SIZE, Hash.FAST_LOAD_FACTOR); + for (BiomeHolder holder : holders) { + Biome biome = holder.getBiome(); + if (biome != null) { + biomes.add(biome); + } + } + this.biomes = biomes; + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 0faa08e..f304287 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -4,7 +4,7 @@ "version": "${version}", "name": "Continuity", - "description": "Continuity is a Fabric mod built around modern APIs to allow for the most efficient connected textures experience possible. It is designed to provide full Optifine parity for all resource packs that use the Optifine CTM format. Continuity also supports Optifine-format emissive textures for block and item models.", + "description": "Continuity is a Fabric mod that allows resource packs that use the OptiFine connected textures format, OptiFine emissive textures format (only for blocks and item models), or OptiFine custom block layers format to work without OptiFine.", "authors": [ "PepperCode1" ],