diff --git a/build.gradle.kts b/build.gradle.kts index 1514de08..afe563f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,6 +64,9 @@ allprojects { // MythicMobs maven("https://mvn.lumine.io/repository/maven-public/") + + // Crunch + maven("https://redempt.dev") } dependencies { @@ -97,6 +100,7 @@ allprojects { shadowJar { relocate("org.bstats", "com.willfp.eco.shaded.bstats") relocate("net.kyori.adventure.text.minimessage", "com.willfp.eco.shaded.minimessage") + relocate("redempt.crunch", "com.willfp.eco.shaded.crunch") } compileJava { diff --git a/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderIntegration.java b/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderIntegration.java index 20654f50..f9865d12 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderIntegration.java +++ b/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderIntegration.java @@ -5,6 +5,9 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; + /** * Wrapper class for placeholder integrations. */ @@ -24,4 +27,14 @@ public interface PlaceholderIntegration extends Integration { */ String translate(@NotNull String text, @Nullable Player player); + + /** + * Find all placeholders in a given text. + * + * @param text The text. + * @return The placeholders. + */ + default List findPlaceholdersIn(@NotNull String text) { + return new ArrayList<>(); + } } diff --git a/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderManager.java b/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderManager.java index 61242c04..127c526f 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderManager.java +++ b/eco-api/src/main/java/com/willfp/eco/core/integrations/placeholder/PlaceholderManager.java @@ -4,8 +4,10 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -80,6 +82,21 @@ public final class PlaceholderManager { return processed; } + /** + * Find all placeholders in a given text. + * + * @param text The text. + * @return The placeholders. + */ + public static List findPlaceholdersIn(@NotNull final String text) { + List found = new ArrayList<>(); + for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) { + found.addAll(integration.findPlaceholdersIn(text)); + } + + return found; + } + private PlaceholderManager() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } diff --git a/eco-api/src/main/java/com/willfp/eco/util/NumberUtils.java b/eco-api/src/main/java/com/willfp/eco/util/NumberUtils.java index b93cb000..b54e73c4 100644 --- a/eco-api/src/main/java/com/willfp/eco/util/NumberUtils.java +++ b/eco-api/src/main/java/com/willfp/eco/util/NumberUtils.java @@ -1,11 +1,15 @@ package com.willfp.eco.util; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.text.DecimalFormat; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.BiFunction; /** * Utilities / API methods for numbers. @@ -16,6 +20,11 @@ public final class NumberUtils { */ private static final double[] SIN_LOOKUP = new double[65536]; + /** + * Crunch handler. + */ + private static BiFunction<@NotNull String, @Nullable Player, @NotNull Double> crunch; + /** * Set of roman numerals to look up. */ @@ -218,6 +227,38 @@ public final class NumberUtils { return formatted.endsWith("00") ? String.valueOf((int) toFormat) : formatted; } + /** + * Evaluate an expression. + * + * @param expression The expression. + * @return The value of the expression. + */ + public static double evaluateExpression(@NotNull final String expression) { + return evaluateExpression(expression, null); + } + + /** + * Evaluate an expression with respect to a player (for placeholders). + * + * @param expression The expression. + * @param player The player. + * @return The value of the expression. + */ + public static double evaluateExpression(@NotNull final String expression, + @Nullable final Player player) { + return crunch.apply(expression, player); + } + + /** + * Init crunch handler. + * + * @param handler The handler. + */ + @ApiStatus.Internal + public static void initCrunch(@NotNull final BiFunction<@NotNull String, @Nullable Player, @NotNull Double> handler) { + crunch = handler; + } + private NumberUtils() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/integrations/PlaceholderIntegrationPAPI.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/integrations/PlaceholderIntegrationPAPI.kt index 41cab144..da6d4c2c 100644 --- a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/integrations/PlaceholderIntegrationPAPI.kt +++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/integrations/PlaceholderIntegrationPAPI.kt @@ -49,4 +49,14 @@ class PlaceholderIntegrationPAPI(private val plugin: EcoPlugin) : PlaceholderExp ): String { return PlaceholderAPI.setPlaceholders(player, text) } + + override fun findPlaceholdersIn(text: String): MutableList { + val placeholders = mutableListOf() + val matcher = PlaceholderAPI.getPlaceholderPattern().matcher(text) + while (matcher.find()) { + placeholders.add(matcher.group()) + } + + return placeholders + } } \ No newline at end of file diff --git a/eco-core/core-plugin/build.gradle b/eco-core/core-plugin/build.gradle index e226aed1..7071667d 100644 --- a/eco-core/core-plugin/build.gradle +++ b/eco-core/core-plugin/build.gradle @@ -5,6 +5,7 @@ dependencies { implementation('net.kyori:adventure-text-minimessage:4.1.0-SNAPSHOT') { exclude group: 'net.kyori', module: 'adventure-api' } + implementation 'com.github.Redempt:Crunch:1.0' compileOnly 'net.kyori:adventure-platform-bukkit:4.0.0' compileOnly 'org.apache.maven:maven-artifact:3.8.1' compileOnly 'com.google.code.gson:gson:2.8.8' diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt index f0a8baf1..120a7f09 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt @@ -95,12 +95,14 @@ import com.willfp.eco.internal.spigot.integrations.hologram.HologramHolographicD import com.willfp.eco.internal.spigot.integrations.mcmmo.McmmoIntegrationImpl import com.willfp.eco.internal.spigot.integrations.multiverseinventories.MultiverseInventoriesIntegration import com.willfp.eco.internal.spigot.integrations.shop.ShopShopGuiPlus +import com.willfp.eco.internal.spigot.math.evaluateExpression import com.willfp.eco.internal.spigot.proxy.BlockBreakProxy import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy import com.willfp.eco.internal.spigot.proxy.SkullProxy import com.willfp.eco.internal.spigot.proxy.TPSProxy import com.willfp.eco.internal.spigot.recipes.ShapedRecipeListener import com.willfp.eco.util.BlockUtils +import com.willfp.eco.util.NumberUtils import com.willfp.eco.util.ServerUtils import com.willfp.eco.util.SkullUtils import net.kyori.adventure.platform.bukkit.BukkitAudiences @@ -155,6 +157,8 @@ abstract class EcoSpigotPlugin : EcoPlugin( val tpsProxy = getProxy(TPSProxy::class.java) ServerUtils.initialize { tpsProxy.getTPS() } + NumberUtils.initCrunch { exp, player -> evaluateExpression(exp, player) } + postInit() } @@ -235,7 +239,7 @@ abstract class EcoSpigotPlugin : EcoPlugin( IntegrationLoader("Alice") { AnticheatManager.register(this, AnticheatAlice()) }, // Custom Entities - IntegrationLoader("MythicMobs") { CustomEntitiesManager.register(CustomEntitiesMythicMobs())}, + IntegrationLoader("MythicMobs") { CustomEntitiesManager.register(CustomEntitiesMythicMobs()) }, // Custom Items IntegrationLoader("Oraxen") { CustomItemsManager.register(CustomItemsOraxen()) }, diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/math/CrunchHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/math/CrunchHandler.kt new file mode 100644 index 00000000..37ba2022 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/math/CrunchHandler.kt @@ -0,0 +1,34 @@ +package com.willfp.eco.internal.spigot.math + +import com.willfp.eco.core.integrations.placeholder.PlaceholderManager +import org.bukkit.entity.Player +import redempt.crunch.CompiledExpression +import redempt.crunch.Crunch +import redempt.crunch.functional.EvaluationEnvironment + +private val cache = mutableMapOf() + +fun evaluateExpression(expression: String, player: Player?): Double { + val placeholderValues = PlaceholderManager.findPlaceholdersIn(expression) + .map { PlaceholderManager.getResult(player, expression).toDouble() } + .toDoubleArray() + val compiled = generateExpression(expression) + return compiled.evaluate(*placeholderValues) +} + +private fun generateExpression(expression: String): CompiledExpression { + val cached = cache[expression] + + if (cached != null) { + return cached + } + + val placeholders = PlaceholderManager.findPlaceholdersIn(expression) + + val env = EvaluationEnvironment() + env.setVariableNames(*placeholders.toTypedArray()) + + val compiled = Crunch.compileExpression(expression, env) + cache[expression] = compiled + return compiled +}