Skip to content

Commit

Permalink
Improved block predicate and block_filter optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
mschae23 committed Oct 13, 2021
1 parent baf5359 commit 8c985c5
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 65 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <origin folder> <target folder>`
`java -jar minecraft-worldgen-updater-1.10.0.jar update features <origin folder> <target folder> --reduced-debug-info`
26 changes: 18 additions & 8 deletions src/main/scala/decorator/definition/BlockFilterDecorator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions src/main/scala/util/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 43 additions & 4 deletions src/main/scala/valueprovider/BlockPredicate.scala
Original file line number Diff line number Diff line change
@@ -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[_]
Expand All @@ -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])
Expand All @@ -39,13 +51,21 @@ 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)

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 {
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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)
}
Expand All @@ -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)
}
40 changes: 19 additions & 21 deletions test/custom/input/block_predicate_optimization.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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"
}
}
]
}
Expand Down
35 changes: 4 additions & 31 deletions test/custom/output/block_predicate_optimization.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 8c985c5

Please sign in to comment.