From c1c49406ba7104a4117d1a2074afc577df155757 Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Fri, 4 Aug 2023 12:15:15 -0600 Subject: [PATCH] Optimize block state predicate and other improvements - Optimize predicate returned by PropertiesParsingHelper.parseBlockStates - Optimize predicate created by BaseCTMProperties.parseBiomes - Avoid slow Guava immutable sets and maps when parsing properties - Fix CustomBlockLayers not resetting disableSolidCheck on resource reload - Do not try to retrieve custom block layer if no layer predicates were defined - Make CTM loading and model wrapping not use static state - Improve how sprites are added to standard overlay emitter - Rename overlay renderer to overlay emitter - Annotate CTMProperties implementation class members with Nullable where appropriate - Remove ApiState.Internal annotations as they were applied to members not in the API - Update Fabric API dependency to use new mod ID and require Fabric API >=0.59.0 - Rewrite README and mod description --- README.md | 18 +- build.gradle | 2 +- .../continuity/api/client/CTMLoader.java | 2 +- .../client/mixin/ModelLoaderMixin.java | 29 +- .../client/mixin/RenderLayersMixin.java | 4 +- .../processor/BaseProcessingPredicate.java | 7 +- .../processor/CompactCTMQuadProcessor.java | 6 +- .../client/processor/ProcessingDataKeys.java | 4 +- .../overlay/OverlayProcessingPredicate.java | 4 +- .../overlay/SimpleOverlayQuadProcessor.java | 27 +- .../overlay/StandardOverlayQuadProcessor.java | 142 ++++--- .../client/properties/BaseCTMProperties.java | 388 +++++++++--------- .../CompactConnectingCTMProperties.java | 4 + .../properties/PropertiesParsingHelper.java | 252 +++++++----- .../properties/RandomCTMProperties.java | 40 +- .../overlay/OverlayPropertiesSection.java | 6 + .../overlay/StandardOverlayCTMProperties.java | 6 + .../client/resource/CTMPropertiesLoader.java | 173 ++++---- .../client/resource/CustomBlockLayers.java | 15 +- .../client/resource/EmissiveIdProvider.java | 2 - .../client/resource/EmissiveSuffixLoader.java | 2 - .../client/resource/ModelWrappingHandler.java | 147 +++---- .../client/resource/ResourcePackUtil.java | 3 - .../continuity/client/util/RenderUtil.java | 5 +- .../client/util/SpriteCalculator.java | 7 +- .../client/util/biome/BiomeHolder.java | 35 +- .../client/util/biome/BiomeHolderManager.java | 38 +- .../client/util/biome/BiomeRetriever.java | 2 - .../client/util/biome/BiomeSetPredicate.java | 35 ++ src/main/resources/fabric.mod.json | 4 +- 30 files changed, 767 insertions(+), 642 deletions(-) create mode 100644 src/main/java/me/pepperbell/continuity/client/util/biome/BiomeSetPredicate.java 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/api/client/CTMLoader.java b/src/main/java/me/pepperbell/continuity/api/client/CTMLoader.java index afb1873..fe04d94 100644 --- a/src/main/java/me/pepperbell/continuity/api/client/CTMLoader.java +++ b/src/main/java/me/pepperbell/continuity/api/client/CTMLoader.java @@ -6,7 +6,7 @@ public interface CTMLoader { QuadProcessorFactory getProcessorFactory(); static CTMLoader of(CTMPropertiesFactory propertiesFactory, QuadProcessorFactory processorFactory) { - return new CTMLoader() { + return new CTMLoader<>() { @Override public CTMPropertiesFactory getPropertiesFactory() { return propertiesFactory; diff --git a/src/main/java/me/pepperbell/continuity/client/mixin/ModelLoaderMixin.java b/src/main/java/me/pepperbell/continuity/client/mixin/ModelLoaderMixin.java index 762d192..bdd0968 100644 --- a/src/main/java/me/pepperbell/continuity/client/mixin/ModelLoaderMixin.java +++ b/src/main/java/me/pepperbell/continuity/client/mixin/ModelLoaderMixin.java @@ -16,8 +16,6 @@ import me.pepperbell.continuity.client.resource.CTMPropertiesLoader; import me.pepperbell.continuity.client.resource.EmissiveSuffixLoader; import me.pepperbell.continuity.client.resource.ModelWrappingHandler; -import me.pepperbell.continuity.client.resource.ResourcePackUtil; -import me.pepperbell.continuity.client.util.biome.BiomeHolderManager; import net.minecraft.block.BlockState; import net.minecraft.client.color.block.BlockColors; import net.minecraft.client.render.model.ModelLoader; @@ -40,18 +38,15 @@ public class ModelLoaderMixin { @Final private Map> spriteAtlasData; + @Unique + private ModelWrappingHandler continuity$wrappingHandler; @Unique private BlockState continuity$currentBlockState; @Inject(method = "(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Lnet/minecraft/util/profiler/Profiler;I)V", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V", args = "ldc=missing_model", shift = At.Shift.BEFORE)) private void continuity$afterStoreArgs(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int mipmap, CallbackInfo ci) { - // TODO: move these to the very beginning of resource reload - ResourcePackUtil.setup(resourceManager); - BiomeHolderManager.clearCache(); - + continuity$wrappingHandler = new ModelWrappingHandler(CTMPropertiesLoader.loadAllWithState(resourceManager)); EmissiveSuffixLoader.load(resourceManager); - CTMPropertiesLoader.clearAll(); - CTMPropertiesLoader.loadAll(resourceManager); } @Inject(method = "method_4716(Lnet/minecraft/block/BlockState;)V", at = @At("HEAD")) @@ -62,24 +57,24 @@ public class ModelLoaderMixin { @Inject(method = "addModel(Lnet/minecraft/client/util/ModelIdentifier;)V", at = @At(value = "TAIL"), locals = LocalCapture.CAPTURE_FAILHARD) private void continuity$afterAddModel(ModelIdentifier id, CallbackInfo ci, UnbakedModel model) { if (continuity$currentBlockState != null) { - ModelWrappingHandler.onAddBlockStateModel(id, continuity$currentBlockState); + if (continuity$wrappingHandler != null) { + continuity$wrappingHandler.onAddBlockStateModel(id, continuity$currentBlockState); + } continuity$currentBlockState = null; } } @Inject(method = "(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Lnet/minecraft/util/profiler/Profiler;I)V", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V", args = "ldc=textures")) private void continuity$onFinishAddingModels(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int mipmap, CallbackInfo ci) { - ModelWrappingHandler.wrapCTMModels(unbakedModels, modelsToBake); + if (continuity$wrappingHandler != null) { + continuity$wrappingHandler.wrapCTMModels(unbakedModels, modelsToBake); + } } @Inject(method = "(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Lnet/minecraft/util/profiler/Profiler;I)V", at = @At("TAIL")) private void continuity$onTailInit(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int mipmap, CallbackInfo ci) { - ModelWrappingHandler.wrapEmissiveModels(spriteAtlasData, unbakedModels, modelsToBake); - - CTMPropertiesLoader.clearAll(); - - // TODO: move these to the very end of resource reload - ResourcePackUtil.clear(); - BiomeHolderManager.refreshHolders(); + if (continuity$wrappingHandler != null) { + continuity$wrappingHandler.wrapEmissiveModels(unbakedModels, modelsToBake, spriteAtlasData); + } } } 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 5bafd49..d70e066 100644 --- a/src/main/java/me/pepperbell/continuity/client/processor/BaseProcessingPredicate.java +++ b/src/main/java/me/pepperbell/continuity/client/processor/BaseProcessingPredicate.java @@ -23,13 +23,18 @@ import net.minecraft.world.biome.Biome; public class BaseProcessingPredicate implements ProcessingPredicate { + @Nullable protected Set matchTilesSet; + @Nullable protected EnumSet faces; + @Nullable protected Predicate biomePredicate; + @Nullable protected IntPredicate heightPredicate; + @Nullable protected Predicate blockEntityNamePredicate; - public BaseProcessingPredicate(Set matchTilesSet, EnumSet faces, Predicate biomePredicate, IntPredicate heightPredicate, Predicate blockEntityNamePredicate) { + public BaseProcessingPredicate(@Nullable Set matchTilesSet, @Nullable EnumSet faces, @Nullable Predicate biomePredicate, @Nullable IntPredicate heightPredicate, @Nullable Predicate blockEntityNamePredicate) { this.matchTilesSet = matchTilesSet; this.faces = faces; this.biomePredicate = biomePredicate; 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 9f906bb..587a48b 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 bfb7a9b..68aa718 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 @@ -5,6 +5,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; @@ -19,7 +21,7 @@ import net.minecraft.world.biome.Biome; public class OverlayProcessingPredicate extends BaseProcessingPredicate { - public OverlayProcessingPredicate(Set matchTilesSet, EnumSet faces, Predicate biomePredicate, IntPredicate heightPredicate, Predicate blockEntityNamePredicate) { + public OverlayProcessingPredicate(@Nullable Set matchTilesSet, @Nullable EnumSet faces, @Nullable Predicate biomePredicate, @Nullable IntPredicate heightPredicate, @Nullable Predicate blockEntityNamePredicate) { super(matchTilesSet, 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 76f8e3d..6092325 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, int processorIndex, 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 291c1d1..2f56c0f 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, int processorIndex, 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 385a3f3..d2b162d 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/BaseCTMProperties.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/BaseCTMProperties.java @@ -13,9 +13,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; @@ -55,12 +56,18 @@ public class BaseCTMProperties implements CTMProperties { protected int packPriority; 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; @@ -203,85 +210,91 @@ 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]; - } + continue; + } catch (NumberFormatException e) { + // + } + } - if (!path.endsWith(".png")) { - path += ".png"; - } + 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 (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 += ".png"; + } - if (!path.startsWith("textures/") && !path.startsWith("optifine/")) { - path = basePath + 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("optifine/")) { - namespace = id.getNamespace(); - } - } else { - if (!path.contains("/")) { - path = "textures/block/" + path; - } else if (!path.startsWith("textures/") && !path.startsWith("optifine/")) { - path = "textures/" + path; - } - } + 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 + "'"); + + 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 + "'"); } } @@ -301,23 +314,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 + "'"); } } } @@ -348,31 +363,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(); } @@ -399,67 +408,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 -> { @@ -491,16 +499,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; } } @@ -586,41 +594,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")) { - ResourcePack pack = ResourcePackUtil.getProvidingResourcePack(resourceId); - if (pack != null && pack != defaultPack) { - valid = false; - break; - } - } else if (packStr.equals("programmer_art")) { - ResourcePack pack = ResourcePackUtil.getProvidingResourcePack(resourceId); - if (pack != null && !pack.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")) { + ResourcePack pack = ResourcePackUtil.getProvidingResourcePack(resourceId); + if (pack != null && pack != defaultPack) { + valid = false; + break; } + } else if (packStr.equals("programmer_art")) { + ResourcePack pack = ResourcePackUtil.getProvidingResourcePack(resourceId); + if (pack != null && !pack.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 + "'"); } } } @@ -685,10 +697,12 @@ public String getMethod() { return method; } + @Nullable public Set getMatchTilesSet() { return matchTilesSet; } + @Nullable public Predicate getMatchBlocksPredicate() { return matchBlocksPredicate; } @@ -697,18 +711,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 26051f9..5a7effd 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/CompactConnectingCTMProperties.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/CompactConnectingCTMProperties.java @@ -2,12 +2,15 @@ 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; import net.minecraft.util.Identifier; public class CompactConnectingCTMProperties extends StandardConnectingCTMProperties { + @Nullable protected Int2IntMap tileReplacementMap; public CompactConnectingCTMProperties(Properties properties, Identifier id, String packName, int packPriority, String method) { @@ -61,6 +64,7 @@ public boolean isValidForMultipass() { return false; } + @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 427505b..2cf9641 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) { + public static Set parseMatchTiles(Properties properties, String propertyKey, Identifier fileLocation, String packName) { String matchTilesStr = properties.getProperty(propertyKey); if (matchTilesStr == null) { return null; @@ -39,67 +45,70 @@ public static ImmutableSet parseMatchTiles(Properties properties, St if (matchTileStrs.length != 0) { String basePath = FilenameUtils.getPath(fileLocation.getPath()); ResourceRedirectHandler redirectHandler = ResourceRedirectHandler.get(); - 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); } + } - try { - setBuilder.add(new Identifier(namespace, path)); + if (path.startsWith("textures/")) { + path = path.substring(9); + } else if (path.startsWith("optifine/")) { + if (redirectHandler == null) { continue; - } catch (InvalidIdentifierException e) { - // } + path = redirectHandler.getSourceSpritePath(path + ".png"); + if (namespace == null) { + namespace = fileLocation.getNamespace(); + } + } else if (!path.contains("/")) { + path = "block/" + path; + } + + 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 @@ -111,33 +120,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 = Registry.BLOCK.get(blockId); - if (block != Blocks.AIR) { + Block block = Registry.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]; @@ -150,21 +164,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 + "'"); @@ -177,48 +190,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 e67f9e2..4b7ffd4 100644 --- a/src/main/java/me/pepperbell/continuity/client/properties/RandomCTMProperties.java +++ b/src/main/java/me/pepperbell/continuity/client/properties/RandomCTMProperties.java @@ -40,30 +40,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 e369f8e..db43fe3 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 b7116c9..3de038e 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 net.minecraft.block.BlockState; @@ -11,7 +13,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, String packName, int packPriority, String method) { @@ -40,10 +44,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/CTMPropertiesLoader.java b/src/main/java/me/pepperbell/continuity/client/resource/CTMPropertiesLoader.java index 79097a9..c0ec2c7 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/CTMPropertiesLoader.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/CTMPropertiesLoader.java @@ -7,18 +7,14 @@ import java.util.Properties; import java.util.function.Consumer; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; - -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectIterator; import me.pepperbell.continuity.api.client.CTMLoader; import me.pepperbell.continuity.api.client.CTMLoaderRegistry; import me.pepperbell.continuity.api.client.CTMProperties; import me.pepperbell.continuity.client.ContinuityClient; import me.pepperbell.continuity.client.util.BooleanState; +import me.pepperbell.continuity.client.util.biome.BiomeHolderManager; import net.minecraft.block.BlockState; import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.resource.ResourceManager; @@ -27,29 +23,40 @@ import net.minecraft.util.Identifier; public final class CTMPropertiesLoader { - private static final List> ALL = new ObjectArrayList<>(); - private static final List> AFFECTS_BLOCK = new ObjectArrayList<>(); - private static final List> IGNORES_BLOCK = new ObjectArrayList<>(); - private static final List> VALID_FOR_MULTIPASS = new ObjectArrayList<>(); + public static LoadingResult loadAllWithState(ResourceManager resourceManager) { + // TODO: move these to the very beginning of resource reload + ResourcePackUtil.setup(resourceManager); + BiomeHolderManager.clearCache(); + + LoadingResult result = loadAll(resourceManager); + + // TODO: move these to the very end of resource reload + ResourcePackUtil.clear(); + BiomeHolderManager.refreshHolders(); + + return result; + } - private static final OptionalListCreator> LIST_CREATOR = new OptionalListCreator<>(); + public static LoadingResult loadAll(ResourceManager resourceManager) { + LoadingData loadingData = new LoadingData(); - @ApiStatus.Internal - public static void loadAll(ResourceManager resourceManager) { int packPriority = 0; Iterator iterator = resourceManager.streamResourcePacks().iterator(); BooleanState invalidIdentifierState = InvalidIdentifierStateHolder.get(); invalidIdentifierState.enable(); while (iterator.hasNext()) { ResourcePack pack = iterator.next(); - loadAll(pack, packPriority); + loadAll(pack, packPriority, loadingData); packPriority++; } invalidIdentifierState.disable(); - resolveMultipassDependents(); + + resolveMultipassDependents(loadingData); + + return new LoadingResult(loadingData.affectsBlock, loadingData.ignoresBlock, loadingData.all.isEmpty()); } - private static void loadAll(ResourcePack pack, int packPriority) { + private static void loadAll(ResourcePack pack, int packPriority, LoadingData loadingData) { String packName = pack.getName(); for (String namespace : pack.getNamespaces(ResourceType.CLIENT_RESOURCES)) { Collection ids = pack.findResources(ResourceType.CLIENT_RESOURCES, namespace, "optifine/ctm", id -> id.getPath().endsWith(".properties")); @@ -57,7 +64,7 @@ private static void loadAll(ResourcePack pack, int packPriority) { try (InputStream stream = pack.open(ResourceType.CLIENT_RESOURCES, id)) { Properties properties = new Properties(); properties.load(stream); - load(properties, id, packName, packPriority); + load(properties, id, packName, packPriority, loadingData); } catch (Exception e) { ContinuityClient.LOGGER.error("Failed to load CTM properties from file '" + id + "' in pack '" + packName + "'", e); } @@ -65,43 +72,46 @@ private static void loadAll(ResourcePack pack, int packPriority) { } } - private static void load(Properties properties, Identifier id, String packName, int packPriority) { + private static void load(Properties properties, Identifier id, String packName, int packPriority, LoadingData loadingData) { String method = properties.getProperty("method", "ctm").trim(); CTMLoader loader = CTMLoaderRegistry.get().getLoader(method); if (loader != null) { - load(loader, properties, id, packName, packPriority, method); + load(loader, properties, id, packName, packPriority, method, loadingData); } else { ContinuityClient.LOGGER.error("Unknown 'method' value '" + method + "' in file '" + id + "' in pack '" + packName + "'"); } } - private static void load(CTMLoader loader, Properties properties, Identifier id, String packName, int packPriority, String method) { + private static void load(CTMLoader loader, Properties properties, Identifier id, String packName, int packPriority, String method, LoadingData loadingData) { T ctmProperties = loader.getPropertiesFactory().createProperties(properties, id, packName, packPriority, method); if (ctmProperties != null) { CTMLoadingContainer container = new CTMLoadingContainer<>(loader, ctmProperties); - ALL.add(container); + loadingData.all.add(container); if (ctmProperties.affectsBlockStates()) { - AFFECTS_BLOCK.add(container); + loadingData.affectsBlock.add(container); } else { - IGNORES_BLOCK.add(container); + loadingData.ignoresBlock.add(container); } if (ctmProperties.affectsTextures() && ctmProperties.isValidForMultipass()) { - VALID_FOR_MULTIPASS.add(container); + loadingData.validForMultipass.add(container); } } } - private static void resolveMultipassDependents() { - if (isEmpty()) { + private static void resolveMultipassDependents(LoadingData loadingData) { + if (loadingData.all.isEmpty()) { return; } + List> all = loadingData.all; + List> validForMultipass = loadingData.validForMultipass; + Object2ObjectOpenHashMap> texture2ContainerMap = new Object2ObjectOpenHashMap<>(); Object2ObjectOpenHashMap>> texture2ContainerListMap = new Object2ObjectOpenHashMap<>(); - int amount = ALL.size(); + int amount = all.size(); for (int i = 0; i < amount; i++) { - CTMLoadingContainer container = ALL.get(i); + CTMLoadingContainer container = all.get(i); Collection textureDependencies = container.getProperties().getTextureDependencies(); for (SpriteIdentifier spriteId : textureDependencies) { Identifier textureId = spriteId.getTextureId(); @@ -123,29 +133,20 @@ private static void resolveMultipassDependents() { } } - int amount1 = VALID_FOR_MULTIPASS.size(); - ObjectIterator>> iterator = texture2ContainerMap.object2ObjectEntrySet().fastIterator(); - while (iterator.hasNext()) { - Object2ObjectMap.Entry> entry = iterator.next(); - Identifier textureId = entry.getKey(); - CTMLoadingContainer container1 = entry.getValue(); - + int amount1 = validForMultipass.size(); + texture2ContainerMap.forEach((textureId, container1) -> { for (int i = 0; i < amount1; i++) { - CTMLoadingContainer container = VALID_FOR_MULTIPASS.get(i); + CTMLoadingContainer container = validForMultipass.get(i); if (container.getProperties().affectsTexture(textureId)) { container1.addMultipassDependent(container); } } - } - ObjectIterator>>> iterator1 = texture2ContainerListMap.object2ObjectEntrySet().fastIterator(); - while (iterator1.hasNext()) { - Object2ObjectMap.Entry>> entry = iterator1.next(); - Identifier textureId = entry.getKey(); - List> containerList = entry.getValue(); + }); + texture2ContainerListMap.forEach((textureId, containerList) -> { int amount2 = containerList.size(); for (int i = 0; i < amount1; i++) { - CTMLoadingContainer container = VALID_FOR_MULTIPASS.get(i); + CTMLoadingContainer container = validForMultipass.get(i); if (container.getProperties().affectsTexture(textureId)) { for (int j = 0; j < amount2; j++) { CTMLoadingContainer container1 = containerList.get(j); @@ -153,77 +154,57 @@ private static void resolveMultipassDependents() { } } } - } + }); for (int i = 0; i < amount; i++) { - CTMLoadingContainer container = ALL.get(i); + CTMLoadingContainer container = all.get(i); container.resolveRecursiveMultipassDependents(); } } - public static void consumeAllAffecting(BlockState state, Consumer> consumer) { - int amount = AFFECTS_BLOCK.size(); - for (int i = 0; i < amount; i++) { - CTMLoadingContainer container = AFFECTS_BLOCK.get(i); - if (container.getProperties().affectsBlockState(state)) { - consumer.accept(container); - } - } + private static class LoadingData { + public final List> all = new ObjectArrayList<>(); + public final List> affectsBlock = new ObjectArrayList<>(); + public final List> ignoresBlock = new ObjectArrayList<>(); + public final List> validForMultipass = new ObjectArrayList<>(); } - @Nullable - public static List> getAllAffecting(BlockState state) { - consumeAllAffecting(state, LIST_CREATOR); - return LIST_CREATOR.get(); - } + public static class LoadingResult { + private final List> affectsBlock; + private final List> ignoresBlock; + private final boolean empty; - public static void consumeAllAffecting(Collection spriteIds, Consumer> consumer) { - int amount = IGNORES_BLOCK.size(); - for (int i = 0; i < amount; i++) { - CTMLoadingContainer container = IGNORES_BLOCK.get(i); - for (SpriteIdentifier spriteId : spriteIds) { - if (container.getProperties().affectsTexture(spriteId.getTextureId())) { + private LoadingResult(List> affectsBlock, List> ignoresBlock, boolean empty) { + this.affectsBlock = affectsBlock; + this.ignoresBlock = ignoresBlock; + this.empty = empty; + } + + public void consumeAllAffecting(BlockState state, Consumer> consumer) { + int amount = affectsBlock.size(); + for (int i = 0; i < amount; i++) { + CTMLoadingContainer container = affectsBlock.get(i); + if (container.getProperties().affectsBlockState(state)) { consumer.accept(container); - break; } } } - } - - @Nullable - public static List> getAllAffecting(Collection spriteIds) { - consumeAllAffecting(spriteIds, LIST_CREATOR); - return LIST_CREATOR.get(); - } - public static boolean isEmpty() { - return ALL.isEmpty(); - } - - @ApiStatus.Internal - public static void clearAll() { - ALL.clear(); - AFFECTS_BLOCK.clear(); - IGNORES_BLOCK.clear(); - VALID_FOR_MULTIPASS.clear(); - } - - private static class OptionalListCreator implements Consumer { - private ObjectArrayList list = null; - - @Override - public void accept(T t) { - if (list == null) { - list = new ObjectArrayList<>(); + public void consumeAllAffecting(Collection spriteIds, Consumer> consumer) { + int amount = ignoresBlock.size(); + for (int i = 0; i < amount; i++) { + CTMLoadingContainer container = ignoresBlock.get(i); + for (SpriteIdentifier spriteId : spriteIds) { + if (container.getProperties().affectsTexture(spriteId.getTextureId())) { + consumer.accept(container); + break; + } + } } - list.add(t); } - @Nullable - public ObjectArrayList get() { - ObjectArrayList list = this.list; - this.list = null; - return list; + public boolean isEmpty() { + return empty; } } } 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/EmissiveIdProvider.java b/src/main/java/me/pepperbell/continuity/client/resource/EmissiveIdProvider.java index ab3d647..c2d6e13 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/EmissiveIdProvider.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/EmissiveIdProvider.java @@ -1,6 +1,5 @@ package me.pepperbell.continuity.client.resource; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import net.fabricmc.loader.api.FabricLoader; @@ -22,7 +21,6 @@ public static Identifier toEmissiveId(Identifier spriteId, String emissiveSuffix return PROVIDER.toEmissiveId(spriteId, emissiveSuffix); } - @ApiStatus.Internal public static void init() { } 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 b228335..6673266 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/ModelWrappingHandler.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/ModelWrappingHandler.java @@ -8,6 +8,8 @@ import java.util.function.Consumer; import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -24,88 +26,92 @@ import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.util.Identifier; -public final class ModelWrappingHandler { - private static final Map MODEL_ID_2_STATE_MAP = new Object2ObjectOpenHashMap<>(); - private static final Map>> MODEL_ID_2_CONTAINERS_MAP = new Object2ObjectOpenHashMap<>(); +public class ModelWrappingHandler { + private final CTMPropertiesLoader.LoadingResult ctmLoadingResult; + + private final Map modelId2StateMap = new Object2ObjectOpenHashMap<>(); + + public ModelWrappingHandler(CTMPropertiesLoader.LoadingResult ctmLoadingResult) { + this.ctmLoadingResult = ctmLoadingResult; + } - public static void onAddBlockStateModel(ModelIdentifier id, BlockState state) { - MODEL_ID_2_STATE_MAP.put(id, state); - List> containerList = CTMPropertiesLoader.getAllAffecting(state); - if (containerList != null) { - MODEL_ID_2_CONTAINERS_MAP.put(id, containerList); + public void onAddBlockStateModel(ModelIdentifier id, BlockState state) { + if (!ctmLoadingResult.isEmpty()) { + modelId2StateMap.put(id, state); } } - public static void wrapCTMModels(Map unbakedModels, Map modelsToBake) { - if (CTMPropertiesLoader.isEmpty()) { - clearMaps(); + public void wrapCTMModels(Map unbakedModels, Map modelsToBake) { + if (ctmLoadingResult.isEmpty()) { return; } Map wrappedModels = new Object2ObjectOpenHashMap<>(); Function unbakedModelGetter = createUnbakedModelGetter(unbakedModels); VoidSet> voidSet = VoidSet.get(); - CollectionBasedConsumer> reusableConsumer = new CollectionBasedConsumer<>(); + OptionalListCreator> listCreator = new OptionalListCreator<>(); modelsToBake.forEach((id, model) -> { - // Only wrap final block state models - if (id instanceof ModelIdentifier modelId && isBlockStateModelId(modelId)) { - Collection dependencies; - try { - dependencies = model.getTextureDependencies(unbakedModelGetter, voidSet); - } catch (ModelNotLoadedException e) { - return; - } + // Only wrap top-level block state models + if (!(id instanceof ModelIdentifier modelId) || !isBlockStateModelId(modelId)) { + return; + } + + BlockState state = modelId2StateMap.get(modelId); + if (state == null) { + return; + } + + Collection spriteIds; + try { + spriteIds = model.getTextureDependencies(unbakedModelGetter, voidSet); + } catch (ModelNotLoadedException e) { + return; + } - List> containerList = MODEL_ID_2_CONTAINERS_MAP.get(modelId); - if (containerList == null) { - containerList = CTMPropertiesLoader.getAllAffecting(dependencies); - if (containerList == null) { - return; + ctmLoadingResult.consumeAllAffecting(state, listCreator); + ctmLoadingResult.consumeAllAffecting(spriteIds, listCreator); + List> containerList = listCreator.get(); + if (containerList == null) { + return; + } + containerList.sort(Collections.reverseOrder()); + + Set> multipassContainerSet = null; + int amount = containerList.size(); + for (int i = 0; i < amount; i++) { + CTMLoadingContainer container = containerList.get(i); + Set> dependents = container.getRecursiveMultipassDependents(); + if (dependents != null) { + if (multipassContainerSet == null) { + multipassContainerSet = new ObjectOpenHashSet<>(); } - } else { - reusableConsumer.setCollection(containerList); - CTMPropertiesLoader.consumeAllAffecting(dependencies, reusableConsumer); + multipassContainerSet.addAll(dependents); } - containerList.sort(Collections.reverseOrder()); - - Set> multipassContainerSet = null; - int amount = containerList.size(); - for (int i = 0; i < amount; i++) { - CTMLoadingContainer container = containerList.get(i); - Set> dependents = container.getRecursiveMultipassDependents(); - if (dependents != null) { - if (multipassContainerSet == null) { - multipassContainerSet = new ObjectOpenHashSet<>(); + } + + List> multipassContainerList = null; + if (multipassContainerSet != null) { + for (CTMLoadingContainer container : multipassContainerSet) { + if (!container.getProperties().affectsBlockStates() || container.getProperties().affectsBlockState(state)) { + if (multipassContainerList == null) { + multipassContainerList = new ObjectArrayList<>(); } - multipassContainerSet.addAll(dependents); + multipassContainerList.add(container); } } - List> multipassContainerList = null; - if (multipassContainerSet != null) { - BlockState state = MODEL_ID_2_STATE_MAP.get(modelId); - for (CTMLoadingContainer container : multipassContainerSet) { - if (!container.getProperties().affectsBlockStates() || container.getProperties().affectsBlockState(state)) { - if (multipassContainerList == null) { - multipassContainerList = new ObjectArrayList<>(); - } - multipassContainerList.add(container); - } - } - if (multipassContainerList != null) { - multipassContainerList.sort(Collections.reverseOrder()); - } + if (multipassContainerList != null) { + multipassContainerList.sort(Collections.reverseOrder()); } - - wrappedModels.put(modelId, new CTMUnbakedModel(model, containerList, multipassContainerList)); } + + wrappedModels.put(modelId, new CTMUnbakedModel(model, containerList, multipassContainerList)); }); - clearMaps(); injectWrappedModels(wrappedModels, unbakedModels, modelsToBake); } - public static void wrapEmissiveModels(Map> spriteAtlasData, Map unbakedModels, Map modelsToBake) { + public void wrapEmissiveModels(Map unbakedModels, Map modelsToBake, Map> spriteAtlasData) { Set spriteIdsToWrap = new ObjectOpenHashSet<>(); spriteAtlasData.forEach((atlasId, pair) -> { @@ -127,14 +133,14 @@ public static void wrapEmissiveModels(Map> voidSet = VoidSet.get(); unbakedModels.forEach((id, model) -> { - Collection dependencies; + Collection spriteIds; try { - dependencies = model.getTextureDependencies(unbakedModelGetter, voidSet); + spriteIds = model.getTextureDependencies(unbakedModelGetter, voidSet); } catch (ModelNotLoadedException e) { return; } - for (SpriteIdentifier spriteId : dependencies) { + for (SpriteIdentifier spriteId : spriteIds) { if (spriteIdsToWrap.contains(spriteId)) { wrappedModels.put(id, new EmissiveUnbakedModel(model)); return; @@ -166,24 +172,25 @@ private static boolean isBlockStateModelId(ModelIdentifier id) { return !id.getVariant().equals("inventory"); } - private static void clearMaps() { - MODEL_ID_2_STATE_MAP.clear(); - MODEL_ID_2_CONTAINERS_MAP.clear(); - } - private static class ModelNotLoadedException extends RuntimeException { } - private static class CollectionBasedConsumer implements Consumer { - private Collection collection; + private static class OptionalListCreator implements Consumer { + private ObjectArrayList list = null; @Override public void accept(T t) { - collection.add(t); + if (list == null) { + list = new ObjectArrayList<>(); + } + list.add(t); } - public void setCollection(Collection collection) { - this.collection = collection; + @Nullable + public ObjectArrayList get() { + ObjectArrayList list = this.list; + this.list = null; + return list; } } } diff --git a/src/main/java/me/pepperbell/continuity/client/resource/ResourcePackUtil.java b/src/main/java/me/pepperbell/continuity/client/resource/ResourcePackUtil.java index 9287008..3fdd633 100644 --- a/src/main/java/me/pepperbell/continuity/client/resource/ResourcePackUtil.java +++ b/src/main/java/me/pepperbell/continuity/client/resource/ResourcePackUtil.java @@ -1,7 +1,6 @@ package me.pepperbell.continuity.client.resource; import org.apache.commons.lang3.ArrayUtils; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import net.minecraft.client.MinecraftClient; @@ -18,7 +17,6 @@ public static DefaultResourcePack getDefaultResourcePack() { return MinecraftClient.getInstance().getResourcePackProvider().getPack(); } - @ApiStatus.Internal public static void setup(ResourceManager resourceManager) { resourcePacks = resourceManager.streamResourcePacks().toArray(ResourcePack[]::new); ArrayUtils.reverse(resourcePacks); @@ -34,7 +32,6 @@ public static ResourcePack getProvidingResourcePack(Identifier resourceId) { return null; } - @ApiStatus.Internal public static void clear() { resourcePacks = null; } 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 1fa7b8c..2eb9218 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.registry.Registry; 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 1de03ca..c9bd9ab 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.util.Identifier; import net.minecraft.util.registry.DynamicRegistryManager; @@ -13,18 +13,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(); @@ -37,26 +37,30 @@ public static void refreshHolders() { return; } - Map compressedIdMap = new Object2ObjectOpenHashMap<>(); + Map compactIdMap = new Object2ObjectOpenHashMap<>(); Registry biomeRegistry = registryManager.get(Registry.BIOME_KEY); 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 1f41f1f..2383b2a 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" ], @@ -32,7 +32,7 @@ "depends": { "minecraft": "1.19.2", - "fabric": ">=0.58.0" + "fabric-api": ">=0.59.0" }, "custom": {