From ebe339063989dd3eefb76162dd8131ba40909187 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 11 Dec 2024 22:03:12 -0500 Subject: [PATCH] feat: Allow block tags in block conditions, add isSolid option feat: Start working on a nicer conditions system feat: `geary test condition` command --- .../location/BlockAboveCondition.kt | 12 ++--- .../location/BlockBelowCondition.kt | 12 ++--- .../conditions/location/BlockCondition.kt | 18 ++----- .../conditions/location/BlockConditions.kt | 20 ++++++++ .../common/conditions/location/CheckResult.kt | 11 +++++ .../common/conditions/location/Checks.kt | 28 +++++++++++ .../location/MaterialOrTagMatcher.kt | 48 +++++++++++++++++++ geary-papermc-mythicmobs/build.gradle.kts | 4 -- .../papermc/plugin/commands/GearyCommands.kt | 1 + .../papermc/plugin/commands/ReloadCommand.kt | 2 +- .../papermc/plugin/commands/TestCommands.kt | 39 +++++++++++++++ 11 files changed, 162 insertions(+), 33 deletions(-) create mode 100644 geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockConditions.kt create mode 100644 geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/CheckResult.kt create mode 100644 geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/Checks.kt create mode 100644 geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/MaterialOrTagMatcher.kt create mode 100644 geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/TestCommands.kt diff --git a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockAboveCondition.kt b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockAboveCondition.kt index d29c207..dd64ccf 100644 --- a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockAboveCondition.kt +++ b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockAboveCondition.kt @@ -4,16 +4,14 @@ import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.actions.Condition import com.mineinabyss.geary.papermc.location import com.mineinabyss.idofront.location.up -import com.mineinabyss.idofront.serialization.MaterialByNameSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import org.bukkit.Material +@JvmInline @Serializable @SerialName("geary:block_above") -class BlockAboveCondition( - val allow: Set<@Serializable(with = MaterialByNameSerializer::class) Material> = setOf(), - val deny: Set<@Serializable(with = MaterialByNameSerializer::class) Material> = setOf(), -) : Condition { - override fun ActionGroupContext.execute(): Boolean = BlockCondition.check(location?.clone()?.up(1), allow, deny) +value class BlockAboveCondition(val conditions: BlockConditions) : Condition { + override fun ActionGroupContext.execute(): Boolean { + return conditions.check(location?.clone()?.up(1)).successOrThrow() + } } diff --git a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockBelowCondition.kt b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockBelowCondition.kt index e13160c..ae9fa6a 100644 --- a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockBelowCondition.kt +++ b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockBelowCondition.kt @@ -4,16 +4,14 @@ import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.actions.Condition import com.mineinabyss.geary.papermc.location import com.mineinabyss.idofront.location.down -import com.mineinabyss.idofront.serialization.MaterialByNameSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import org.bukkit.Material +@JvmInline @Serializable @SerialName("geary:block_below") -class BlockBelowCondition( - val allow: Set<@Serializable(with = MaterialByNameSerializer::class) Material> = setOf(), - val deny: Set<@Serializable(with = MaterialByNameSerializer::class) Material> = setOf(), -) : Condition { - override fun ActionGroupContext.execute(): Boolean = BlockCondition.check(location?.clone()?.down(1), allow, deny) +value class BlockBelowCondition(val conditions: BlockConditions) : Condition { + override fun ActionGroupContext.execute(): Boolean { + return conditions.check(location?.clone()?.down(1)).successOrThrow() + } } diff --git a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockCondition.kt b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockCondition.kt index c7549e7..2c83cea 100644 --- a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockCondition.kt +++ b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockCondition.kt @@ -3,24 +3,14 @@ package com.mineinabyss.geary.papermc.features.common.conditions.location import com.mineinabyss.geary.actions.ActionGroupContext import com.mineinabyss.geary.actions.Condition import com.mineinabyss.geary.papermc.location -import com.mineinabyss.idofront.serialization.MaterialByNameSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import org.bukkit.Location -import org.bukkit.Material +@JvmInline @Serializable @SerialName("geary:block") -class BlockCondition( - val allow: Set<@Serializable(with = MaterialByNameSerializer::class) Material> = setOf(), - val deny: Set<@Serializable(with = MaterialByNameSerializer::class) Material> = setOf(), -) : Condition { - override fun ActionGroupContext.execute(): Boolean = check(location, allow, deny) - - companion object { - fun check(location: Location?, allow: Set, deny: Set): Boolean { - val blockBelow = location?.block?.type ?: return true - return (allow.isEmpty() || blockBelow in allow) && blockBelow !in deny - } +value class BlockCondition(val conditions: BlockConditions) : Condition { + override fun ActionGroupContext.execute(): Boolean { + return conditions.check(location?.clone()).successOrThrow() } } diff --git a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockConditions.kt b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockConditions.kt new file mode 100644 index 0000000..408203e --- /dev/null +++ b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/BlockConditions.kt @@ -0,0 +1,20 @@ +package com.mineinabyss.geary.papermc.features.common.conditions.location + +import kotlinx.serialization.Serializable +import org.bukkit.Location + +@Serializable +data class BlockConditions( + val allow: MaterialOrTagMatcher? = null, + val deny: MaterialOrTagMatcher? = null, + val isSolid: Boolean? = null, +) { + fun check(location: Location?): CheckResult { + val material = location?.block?.type ?: return CheckResult.Success + return checks { + checkOptional("Allowed materials", allow) { it.matches(material) } + checkOptional("Denied materials", deny) { !it.matches(material) } + checkOptional("Solid", isSolid) { it == material.isSolid } + } + } +} diff --git a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/CheckResult.kt b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/CheckResult.kt new file mode 100644 index 0000000..ea48ba7 --- /dev/null +++ b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/CheckResult.kt @@ -0,0 +1,11 @@ +package com.mineinabyss.geary.papermc.features.common.conditions.location + +sealed interface CheckResult { + data object Success : CheckResult + data class Failure(val message: String) : CheckResult + + fun successOrThrow() = when (this) { + is Success-> true + is Failure -> error(message) + } +} diff --git a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/Checks.kt b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/Checks.kt new file mode 100644 index 0000000..edb6e63 --- /dev/null +++ b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/Checks.kt @@ -0,0 +1,28 @@ +package com.mineinabyss.geary.papermc.features.common.conditions.location + +class Checks() { + var failed = StringBuilder() + + inline fun check(name: String, run: () -> Boolean) { + val result = runCatching { run() } + .onFailure { exception -> + with(failed) { + appendLine("$name: ${exception.message}") + } + + } + .getOrNull() ?: return + if (!result) failed.appendLine("$name: Condition failed") + } + + inline fun checkOptional(name: String, argument: T?, run: (T) -> Boolean) { + if (argument == null) return + check(name) { run(argument) } + } + + val result get() = if(failed.isEmpty()) CheckResult.Success else CheckResult.Failure(failed.toString()) +} + +inline fun checks(check: Checks.() -> Unit): CheckResult { + return Checks().apply { check() }.result +} diff --git a/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/MaterialOrTagMatcher.kt b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/MaterialOrTagMatcher.kt new file mode 100644 index 0000000..0c787a9 --- /dev/null +++ b/geary-papermc-features/src/main/kotlin/com/mineinabyss/geary/papermc/features/common/conditions/location/MaterialOrTagMatcher.kt @@ -0,0 +1,48 @@ +package com.mineinabyss.geary.papermc.features.common.conditions.location + +import com.mineinabyss.geary.serialization.serializers.InnerSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.serializer +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.Tag + +//TODO probably useful in idofront? +/** + * Serializable class for matching materials and tags, uses a string list where tags start with # + */ +@Serializable(with = MaterialOrTagMatcher.Serializer::class) +data class MaterialOrTagMatcher( + val materials: List, + val tags: List>, +) { + fun matches(material: Material) = + (materials.isEmpty() || material in materials) && (tags.isEmpty() || tags.any { it.isTagged(material) }) + + object Serializer : InnerSerializer, MaterialOrTagMatcher>( + "MaterialOrTagMatcher", + ListSerializer(String.Companion.serializer()), + { Serializer.decodeList(it) }, + { Serializer.encodeList(it) } + ) { + fun encodeList(matcher: MaterialOrTagMatcher): List { + return matcher.materials.map { it.key.toString() } + matcher.tags.map { "#" + it.key.asMinimalString() } + } + + fun decodeList(list: List): MaterialOrTagMatcher { + val (tags, materials) = list.partition { it.startsWith("#") } + return MaterialOrTagMatcher( + materials.mapNotNull { Material.getMaterial(it) }, + tags.mapNotNull { + Bukkit.getTag( + Tag.REGISTRY_BLOCKS, + NamespacedKey.fromString(it.removePrefix("#")) ?: return@mapNotNull null, + Material::class.java + ) + } + ) + } + } +} diff --git a/geary-papermc-mythicmobs/build.gradle.kts b/geary-papermc-mythicmobs/build.gradle.kts index b97a2d8..dbcc093 100644 --- a/geary-papermc-mythicmobs/build.gradle.kts +++ b/geary-papermc-mythicmobs/build.gradle.kts @@ -20,7 +20,3 @@ dependencies { implementation(project(":geary-papermc-tracking")) implementation(project(":geary-papermc-features")) } - -/*configurations.all { - resolutionStrategy.cacheChangingModulesFor( 0, "seconds") -}*/ diff --git a/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/GearyCommands.kt b/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/GearyCommands.kt index 96df8b0..4c1c763 100644 --- a/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/GearyCommands.kt +++ b/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/GearyCommands.kt @@ -12,6 +12,7 @@ internal fun GearyPluginImpl.registerGearyCommands() = commands { prefabs() reload(this@registerGearyCommands) debug() + testCommands() } } diff --git a/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/ReloadCommand.kt b/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/ReloadCommand.kt index 341e440..081666a 100644 --- a/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/ReloadCommand.kt +++ b/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/ReloadCommand.kt @@ -6,7 +6,6 @@ import com.mineinabyss.geary.papermc.gearyPaper import com.mineinabyss.geary.papermc.plugin.GearyPluginImpl import com.mineinabyss.geary.papermc.spawning.SpawningFeature import com.mineinabyss.geary.prefabs.Prefabs -import com.mineinabyss.geary.prefabs.prefabs import com.mineinabyss.idofront.commands.brigadier.IdoCommand @@ -22,4 +21,5 @@ internal fun IdoCommand.reload(plugin: GearyPluginImpl) = "reload" { } "recipes" { executes { plugin.features.reload(sender) } } "spawns" { executes { plugin.features.reload(sender) } } + "commands" { executes { }} } diff --git a/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/TestCommands.kt b/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/TestCommands.kt new file mode 100644 index 0000000..f0a7e7d --- /dev/null +++ b/geary-papermc-plugin/src/main/kotlin/com/mineinabyss/geary/papermc/plugin/commands/TestCommands.kt @@ -0,0 +1,39 @@ +package com.mineinabyss.geary.papermc.plugin.commands + +import com.mineinabyss.geary.actions.ActionGroupContext +import com.mineinabyss.geary.actions.Condition +import com.mineinabyss.geary.papermc.gearyPaper +import com.mineinabyss.geary.papermc.tracking.entities.toGeary +import com.mineinabyss.geary.serialization.SerializableComponents +import com.mineinabyss.geary.serialization.serializers.PolymorphicListAsMapSerializer +import com.mineinabyss.idofront.commands.brigadier.Args +import com.mineinabyss.idofront.commands.brigadier.IdoCommand +import com.mineinabyss.idofront.commands.brigadier.context.IdoPlayerCommandContext +import com.mineinabyss.idofront.commands.brigadier.playerExecutes +import com.mineinabyss.idofront.messaging.error +import com.mineinabyss.idofront.messaging.success + +fun IdoCommand.testCommands() = "test" { + requiresPermission("geary.admin.test") + "condition" { + playerExecutes(Args.greedyString()) { yaml -> + test(yaml) + } + } +} + +fun IdoPlayerCommandContext.test(yaml: String) { + val decoded = gearyPaper.worldManager.global.getAddon(SerializableComponents) + .formats["yml"] + ?.decodeFromString(PolymorphicListAsMapSerializer.ofComponents(), yaml) + ?: fail("Could not decode yaml") + decoded.forEach { condition -> + if (condition !is Condition) return@forEach + val conditionName = condition::class.simpleName ?: return@forEach + with(condition) { + runCatching { ActionGroupContext(player.toGeary()).execute() } + .onSuccess { sender.success("Condition $conditionName passed") } + .onFailure { exception -> sender.error("Condition $conditionName failed:\n${exception.message}") } + } + } +}