Skip to content

Commit

Permalink
feat: Allow block tags in block conditions, add isSolid option
Browse files Browse the repository at this point in the history
feat: Start working on a nicer conditions system
feat: `geary test condition` command
  • Loading branch information
0ffz committed Dec 12, 2024
1 parent 011be53 commit ebe3390
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Material>, deny: Set<Material>): 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()
}
}
Original file line number Diff line number Diff line change
@@ -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 }
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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 <T> 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
}
Original file line number Diff line number Diff line change
@@ -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<Material>,
val tags: List<Tag<Material>>,
) {
fun matches(material: Material) =
(materials.isEmpty() || material in materials) && (tags.isEmpty() || tags.any { it.isTagged(material) })

object Serializer : InnerSerializer<List<String>, MaterialOrTagMatcher>(
"MaterialOrTagMatcher",
ListSerializer(String.Companion.serializer()),
{ Serializer.decodeList(it) },
{ Serializer.encodeList(it) }
) {
fun encodeList(matcher: MaterialOrTagMatcher): List<String> {
return matcher.materials.map { it.key.toString() } + matcher.tags.map { "#" + it.key.asMinimalString() }
}

fun decodeList(list: List<String>): MaterialOrTagMatcher {
val (tags, materials) = list.partition { it.startsWith("#") }
return MaterialOrTagMatcher(
materials.mapNotNull { Material.getMaterial(it) },
tags.mapNotNull {
Bukkit.getTag<Material>(
Tag.REGISTRY_BLOCKS,
NamespacedKey.fromString(it.removePrefix("#")) ?: return@mapNotNull null,
Material::class.java
)
}
)
}
}
}
4 changes: 0 additions & 4 deletions geary-papermc-mythicmobs/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,3 @@ dependencies {
implementation(project(":geary-papermc-tracking"))
implementation(project(":geary-papermc-features"))
}

/*configurations.all {
resolutionStrategy.cacheChangingModulesFor( 0, "seconds")
}*/
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal fun GearyPluginImpl.registerGearyCommands() = commands {
prefabs()
reload(this@registerGearyCommands)
debug()
testCommands()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -22,4 +21,5 @@ internal fun IdoCommand.reload(plugin: GearyPluginImpl) = "reload" {
}
"recipes" { executes { plugin.features.reload<RecipeFeature>(sender) } }
"spawns" { executes { plugin.features.reload<SpawningFeature>(sender) } }
"commands" { executes { }}
}
Original file line number Diff line number Diff line change
@@ -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}") }
}
}
}

0 comments on commit ebe3390

Please sign in to comment.