From 8c985c54c6c6bf771f8f92c348d2809f5e98e715 Mon Sep 17 00:00:00 2001 From: mschae23 <46165762+mschae23@users.noreply.github.com> Date: Wed, 13 Oct 2021 15:39:19 +0200 Subject: [PATCH] Improved block predicate and block_filter optimization --- README.md | 2 +- .../definition/BlockFilterDecorator.scala | 26 ++++++---- src/main/scala/util/Utils.scala | 8 ++++ .../scala/valueprovider/BlockPredicate.scala | 47 +++++++++++++++++-- .../input/block_predicate_optimization.json | 40 ++++++++-------- .../output/block_predicate_optimization.json | 35 ++------------ 6 files changed, 93 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index b37159d..bfa4a86 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ This tool can update configured features to a newer Minecraft version. ### Usage -`java -jar minecraft-worldgen-updater-1.7.0.jar update features ` +`java -jar minecraft-worldgen-updater-1.10.0.jar update features --reduced-debug-info` diff --git a/src/main/scala/decorator/definition/BlockFilterDecorator.scala b/src/main/scala/decorator/definition/BlockFilterDecorator.scala index 9cfc27d..99b82d7 100644 --- a/src/main/scala/decorator/definition/BlockFilterDecorator.scala +++ b/src/main/scala/decorator/definition/BlockFilterDecorator.scala @@ -4,7 +4,7 @@ package decorator.definition import cats.data.Writer import de.martenschaefer.data.serialization.{ Codec, ElementNode, ValidationError } import decorator.definition.BlockFilterDecoratorConfig.Old1 -import decorator.{ Decorator, Decorators } +import decorator.{ ConfiguredDecorator, Decorator, Decorators } import feature.definition.DecoratedFeatureConfig import feature.{ ConfiguredFeature, FeatureProcessResult, Features } import valueprovider.{ AllOfBlockPredicate, BlockPredicate, MatchingBlocksBlockPredicate, NotBlockPredicate, TrueBlockPredicate } @@ -17,20 +17,30 @@ case object BlockFilterDecorator extends Decorator(Codec[BlockFilterDecoratorCon else if (!context.onlyUpdate) config.predicate.process match { case TrueBlockPredicate => Writer(List.empty, feature) - case NotBlockPredicate(TrueBlockPredicate) => Writer(List( - ValidationError(path => s"$path: Block filter uses not(true) predicate; " + case NotBlockPredicate(TrueBlockPredicate) => mergeBlockFilter(feature, NotBlockPredicate(TrueBlockPredicate)) + .mapWritten(ValidationError(path => s"$path: Block filter uses not(true) predicate; " + "the decorated feature will never generate", List(ElementNode.Name("config"), - ElementNode.Name("predicate")))), Features.DECORATED.configure( - DecoratedFeatureConfig(feature, this.configure(config)))) + ElementNode.Name("predicate"))) :: _) - case predicate => Writer(List.empty, Features.DECORATED.configure( - DecoratedFeatureConfig(feature, this.configure( - BlockFilterDecoratorConfig(predicate))))) + case predicate => mergeBlockFilter(feature, predicate) } else super.process(config, feature, context) } + def mergeBlockFilter(feature: ConfiguredFeature[_, _], predicate: BlockPredicate): FeatureProcessResult = + feature match { + case ConfiguredFeature(Features.DECORATED, DecoratedFeatureConfig( + innerFeature, ConfiguredDecorator(Decorators.BLOCK_FILTER, + BlockFilterDecoratorConfig(predicate2, _)))) => + Writer(List.empty, Features.DECORATED.configure( + DecoratedFeatureConfig(innerFeature, this.configure( + BlockFilterDecoratorConfig(AllOfBlockPredicate(List(predicate, predicate2)).process))))) + case _ => Writer(List.empty, Features.DECORATED.configure( + DecoratedFeatureConfig(feature, this.configure( + BlockFilterDecoratorConfig(predicate))))) + } + def updateOld1(old1: Old1): BlockPredicate = { if (old1.allowed.isEmpty && old1.disallowed.isEmpty) return AllOfBlockPredicate(List.empty) diff --git a/src/main/scala/util/Utils.scala b/src/main/scala/util/Utils.scala index 07f1833..4be92c3 100644 --- a/src/main/scala/util/Utils.scala +++ b/src/main/scala/util/Utils.scala @@ -14,6 +14,14 @@ extension [T](self: T) { } } +extension [T](self: List[T]) { + def uniquePairs: List[(T, T)] = for { + (x, idxX) <- self.zipWithIndex + (y, idxY) <- self.zipWithIndex + if idxX < idxY + } yield (x, y) +} + def colored(text: String, color: String)(using flags: Map[Flag, Boolean]): String = { if (!flags(Flag.Colored)) text diff --git a/src/main/scala/valueprovider/BlockPredicate.scala b/src/main/scala/valueprovider/BlockPredicate.scala index 7675dbe..d1b92cc 100644 --- a/src/main/scala/valueprovider/BlockPredicate.scala +++ b/src/main/scala/valueprovider/BlockPredicate.scala @@ -1,12 +1,13 @@ package de.martenschaefer.minecraft.worldgenupdater package valueprovider +import java.util.Objects import de.martenschaefer.data.registry.Registry import de.martenschaefer.data.registry.Registry.register import de.martenschaefer.data.registry.impl.SimpleRegistry import de.martenschaefer.data.serialization.Codec import de.martenschaefer.data.util.Identifier -import util.{ BlockPos, BlockState } +import util.* trait BlockPredicate { def predicateType: BlockPredicateType[_] @@ -22,6 +23,17 @@ object BlockPredicate { val MATCHING_AIR = MatchingBlocksBlockPredicate(List(Identifier("minecraft", "air")), BlockPos.ORIGIN) val MATCHING_AIR_OR_WATER = MatchingBlocksBlockPredicate(List(Identifier("minecraft", "air"), Identifier("minecraft", "water")), BlockPos.ORIGIN) + + def hasTruePredicate(predicates: List[BlockPredicate]): Boolean = + predicates.contains(TrueBlockPredicate) || predicates.uniquePairs.exists(isAlwaysTruePair) + + def isAlwaysTruePair(a: BlockPredicate, b: BlockPredicate): Boolean = a match { + case NotBlockPredicate(predicateA) => b == predicateA + case _ => b match { + case NotBlockPredicate(predicateB) => a == predicateB + case _ => false + } + } } case class BlockPredicateType[P <: BlockPredicate](val codec: Codec[P]) @@ -39,6 +51,7 @@ object BlockPredicateTypes { val ALL_OF = register("all_of", Codec[AllOfBlockPredicate]) val NOT = register("not", Codec[NotBlockPredicate]) val TRUE = register("true", Codec[TrueBlockPredicate.type]) + val FALSE = registerCustom("false", Codec[FalseBlockPredicate.type]) private def register[P <: BlockPredicate](name: String, codec: Codec[P]): BlockPredicateType[P] = { val predicateType = BlockPredicateType(codec) @@ -46,6 +59,13 @@ object BlockPredicateTypes { predicateType.register(Identifier("minecraft", name)) predicateType } + + private def registerCustom[P <: BlockPredicate](name: String, codec: Codec[P]): BlockPredicateType[P] = { + val predicateType = BlockPredicateType(codec) + + predicateType.register(Identifier(UpdaterMain.NAMESPACE, name)) + predicateType + } } case class MatchingBlocksBlockPredicate(val blocks: List[Identifier], val offset: BlockPos) extends BlockPredicate { @@ -102,19 +122,21 @@ object WouldSurviveBlockPredicate { case class AnyOfBlockPredicate(val predicates: List[BlockPredicate]) extends BlockPredicate derives Codec { override val predicateType: BlockPredicateType[_] = BlockPredicateTypes.ANY_OF - override def process: BlockPredicate = this.predicates.map(_.process) match { + override def process: BlockPredicate = this.predicates.map(_.process).distinct match { case predicate :: Nil => predicate.process case Nil => NotBlockPredicate(TrueBlockPredicate).process - case predicates if predicates.contains(TrueBlockPredicate) => TrueBlockPredicate.process + case predicates if BlockPredicate.hasTruePredicate(predicates) => TrueBlockPredicate.process case predicates if predicates.exists(_ match { case AnyOfBlockPredicate(_) => true case NotBlockPredicate(AllOfBlockPredicate(_)) => true + case FalseBlockPredicate => true case _ => false }) => AnyOfBlockPredicate(predicates.flatMap(_ match { case AnyOfBlockPredicate(predicates) => predicates case NotBlockPredicate(AllOfBlockPredicate(predicates)) => predicates.map(NotBlockPredicate(_)).map(_.process) + case FalseBlockPredicate => List.empty case other => List(other) })).process @@ -125,11 +147,13 @@ case class AnyOfBlockPredicate(val predicates: List[BlockPredicate]) extends Blo case class AllOfBlockPredicate(val predicates: List[BlockPredicate]) extends BlockPredicate derives Codec { override val predicateType: BlockPredicateType[_] = BlockPredicateTypes.ALL_OF - override def process: BlockPredicate = this.predicates.map(_.process) match { + override def process: BlockPredicate = this.predicates.map(_.process).distinct match { case predicate :: Nil => predicate.process case Nil => TrueBlockPredicate case predicates if predicates.contains(TrueBlockPredicate) => AllOfBlockPredicate(predicates .filter(_ != TrueBlockPredicate)).process + case predicates if predicates.contains(NotBlockPredicate(TrueBlockPredicate)) => + FalseBlockPredicate.process case predicates if predicates.exists(_ match { case AllOfBlockPredicate(_) => true case NotBlockPredicate(AnyOfBlockPredicate(_)) => true @@ -141,6 +165,8 @@ case class AllOfBlockPredicate(val predicates: List[BlockPredicate]) extends Blo predicates.map(NotBlockPredicate(_)).map(_.process) case other => List(other) })).process + case predicates if BlockPredicate.hasTruePredicate(predicates) => + FalseBlockPredicate.process case predicates => AllOfBlockPredicate(predicates) } @@ -161,3 +187,16 @@ case object TrueBlockPredicate extends BlockPredicate { given Codec[TrueBlockPredicate.type] = Codec.unit(TrueBlockPredicate) } + +case object FalseBlockPredicate extends BlockPredicate { + override def predicateType: BlockPredicateType[_] = BlockPredicateTypes.FALSE + + given Codec[FalseBlockPredicate.type] = Codec.unit(FalseBlockPredicate) + + override def process: BlockPredicate = NotBlockPredicate(TrueBlockPredicate).process + + override def equals(o: Any): Boolean = Objects.equals(this, o) + || NotBlockPredicate(TrueBlockPredicate) == o + + def unapply(predicate: BlockPredicate): Boolean = equals(predicate) +} diff --git a/test/custom/input/block_predicate_optimization.json b/test/custom/input/block_predicate_optimization.json index 77a3eb3..49f915a 100644 --- a/test/custom/input/block_predicate_optimization.json +++ b/test/custom/input/block_predicate_optimization.json @@ -2,8 +2,26 @@ "config": { "feature": { "config": { + "feature": { + "config": {}, + "type": "no_op" + }, + "decorator": { + "config": { + "predicate": { + "blocks": [ + "minecraft:grass_block" + ], + "offset": [ + 0, -1, 0 + ], + "type": "minecraft:matching_blocks" + } + }, + "type": "minecraft:block_filter" + } }, - "type": "minecraft:no_op" + "type": "minecraft:decorated" }, "decorator": { "config": { @@ -15,26 +33,6 @@ }, { "type": "minecraft:replaceable" - }, - { - "type": "not", - "predicate": { - "predicates": [ - { - "type": "minecraft:replaceable" - }, - { - "type": "minecraft:replaceable" - }, - { - "type": "not", - "predicate": { - "type": "minecraft:replaceable" - } - } - ], - "type": "any_of" - } } ] } diff --git a/test/custom/output/block_predicate_optimization.json b/test/custom/output/block_predicate_optimization.json index 484aaef..176a3bc 100644 --- a/test/custom/output/block_predicate_optimization.json +++ b/test/custom/output/block_predicate_optimization.json @@ -17,42 +17,15 @@ "type": "minecraft:replaceable" }, { - "offset": [ - 0, - 0, - 0 + "blocks": [ + "minecraft:grass_block" ], - "type": "minecraft:replaceable" - }, - { - "predicate": { - "offset": [ - 0, - 0, - 0 - ], - "type": "minecraft:replaceable" - }, - "type": "minecraft:not" - }, - { - "predicate": { - "offset": [ - 0, - 0, - 0 - ], - "type": "minecraft:replaceable" - }, - "type": "minecraft:not" - }, - { "offset": [ 0, - 0, + -1, 0 ], - "type": "minecraft:replaceable" + "type": "minecraft:matching_blocks" } ], "type": "minecraft:all_of"