Compare commits

..

68 Commits

Author SHA1 Message Date
Auxilor
ed41c1091c Suppression 2023-07-31 16:17:49 +02:00
Auxilor
4fcf8c0368 Updated to 6.65.4 2023-07-31 16:13:16 +02:00
Auxilor
4ae4af6f1d Fixed UltraEconomy 2023-07-31 16:10:32 +02:00
Auxilor
bba0900183 Fixed UltraEconomy build 2023-07-31 16:04:23 +02:00
Auxilor
641aee3965 Patched an exploit 2023-07-31 15:38:42 +02:00
Auxilor
18ea6cc568 Clarifications and fixes for other commits 2023-07-31 15:14:01 +02:00
Auxilor
8335f6bae5 Added quick patch for an exploit 2023-07-31 15:13:45 +02:00
Auxilor
514a8e76f9 Revert "Reworked EntityDamageByEntityEvent, finally"
This reverts commit 5cfb87e0
2023-07-31 15:12:48 +02:00
_OfTeN_
f34af36690 Added ArmorTrim arg parser (now without NMS) 2023-07-30 21:27:09 +03:00
_OfTeN_
143148d93e Made backend use 1.20 API 2023-07-30 21:26:21 +03:00
_OfTeN_
4f6c023217 Stupidity fix 2023-07-30 20:44:45 +03:00
_OfTeN_
620e1d3042 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt
#	eco-core/core-plugin/src/main/resources/config.yml
2023-07-30 20:39:00 +03:00
_OfTeN_
d3c831c19b Added a config option to enforce additional crafting recipe check (in case you're encountering issues with 3d party custom item plugins like ItemsAdder or Oraxen) 2023-07-30 20:38:09 +03:00
Auxilor
22d9dbdf66 Added display-without-meta option 2023-07-22 16:13:31 +01:00
Auxilor
238ea86e09 Merge branch 'master' into develop 2023-07-22 14:16:42 +01:00
Auxilor
5cfb87e03c Reworked EntityDamageByEntityEvent, finally 2023-07-22 14:16:34 +01:00
Auxilor
ff61527939 Updated to 6.65.3 2023-07-20 13:01:19 +01:00
Auxilor
7ff578f89b Fixed clearInjectedPlaceholders 2023-07-20 12:36:41 +01:00
Auxilor
27da03e8db Optimised config hashing 2023-07-20 12:19:10 +01:00
Auxilor
f1bef38046 Updated to 6.65.2 2023-07-16 13:03:50 +01:00
Auxilor
ee237f9c58 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-api/src/main/java/com/willfp/eco/core/config/interfaces/Config.java
2023-07-16 13:01:28 +01:00
Auxilor
0a80518755 Merge remote-tracking branch 'origin/develop' into develop 2023-07-13 15:17:13 +01:00
Auxilor
134859fea0 Extremely obvious optimisation to expression parsing 2023-07-13 15:16:23 +01:00
_OfTeN_
5c267d500a Fixed head arg parser breaking back serialization when used with texture 2023-07-12 23:42:52 +03:00
Auxilor
4c9735583c Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/math/ExpressionHandlers.kt
2023-07-10 15:07:15 +01:00
Auxilor
739d9cfffb Optimised Config#getDoubleFromExpression for constant values 2023-07-06 17:35:52 +01:00
Auxilor
4301d83e91 Removed ItemMetaProxy, can be reimplemented much better without NMS 2023-07-06 17:33:44 +01:00
Auxilor
84d5c5e665 Merge branch 'master' into develop 2023-07-05 15:33:07 +01:00
Auxilor
50f217eebe Switched to a standard map for LazyPlaceholderTranslationExpressionHandler 2023-07-05 15:30:06 +01:00
Auxilor
041fce69cc Updated gradle 2023-07-05 15:00:34 +01:00
Auxilor
238bd08502 Merge remote-tracking branch 'origin/dependabot/gradle/org.javassist-javassist-3.29.2-GA' 2023-07-05 14:51:57 +01:00
Auxilor
940e6e8b5b Merge remote-tracking branch 'origin/dependabot/gradle/io.lumine-Mythic-5.3.5' 2023-07-05 14:51:56 +01:00
Auxilor
eb07622496 Merge remote-tracking branch 'origin/dependabot/gradle/com.github.johnrengelman.shadow-8.1.1' 2023-07-05 14:51:54 +01:00
Auxilor
841ed52bdd Merge remote-tracking branch 'origin/dependabot/gradle/com.arcaniax-HeadDatabase-API-1.3.1' 2023-07-05 14:51:52 +01:00
Auxilor
342f6dbc7b Updated to 6.65.1 2023-07-05 14:44:31 +01:00
Auxilor
245b577adc Expressions that are just numbers will be immediately converted rather than being evaluated 2023-07-05 14:44:22 +01:00
dependabot[bot]
1258305755 Bump io.lumine:Mythic from 5.2.1 to 5.3.5
Bumps io.lumine:Mythic from 5.2.1 to 5.3.5.

---
updated-dependencies:
- dependency-name: io.lumine:Mythic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-04 21:35:54 +00:00
_OfTeN_
cd8ed5a823 Implemented ItemMetaProxy with an ability to set/get trim from the 1.20 armor meta
Added an ArgParserTrim to add trims to armor via item lookup strings
2023-06-24 16:38:10 +03:00
_OfTeN_
795ead57bf Fixed MythicMobs mob test predicate 2023-06-23 11:24:03 +03:00
_OfTeN_
910cdaf992 Updated KingdomsX antigrief integration 2023-06-23 10:41:15 +03:00
_OfTeN_
85c02d3402 Merge remote-tracking branch 'origin/master' into develop 2023-06-23 09:42:26 +03:00
dependabot[bot]
b4ad2fd4d7 Bump com.github.johnrengelman.shadow from 7.1.2 to 8.1.1
Bumps com.github.johnrengelman.shadow from 7.1.2 to 8.1.1.

---
updated-dependencies:
- dependency-name: com.github.johnrengelman.shadow
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 22:00:27 +00:00
dependabot[bot]
3c5190da3a Bump com.arcaniax:HeadDatabase-API from 1.3.0 to 1.3.1
Bumps [com.arcaniax:HeadDatabase-API](https://github.com/Arcaniax-Development/HeadDatabase-API) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/Arcaniax-Development/HeadDatabase-API/releases)
- [Commits](https://github.com/Arcaniax-Development/HeadDatabase-API/compare/1.3.0...1.3.1)

---
updated-dependencies:
- dependency-name: com.arcaniax:HeadDatabase-API
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 21:59:50 +00:00
dependabot[bot]
0f0f003f23 Bump org.javassist:javassist from 3.28.0-GA to 3.29.2-GA
Bumps [org.javassist:javassist](https://github.com/jboss-javassist/javassist) from 3.28.0-GA to 3.29.2-GA.
- [Release notes](https://github.com/jboss-javassist/javassist/releases)
- [Changelog](https://github.com/jboss-javassist/javassist/blob/master/Changes.md)
- [Commits](https://github.com/jboss-javassist/javassist/commits)

---
updated-dependencies:
- dependency-name: org.javassist:javassist
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 21:59:28 +00:00
Auxilor
7e5e5162c3 LazyPlaceholderTranslationExpressionHandler now uses a regular map rather than a caffeine cache 2023-06-18 22:03:48 +02:00
Auxilor
09417d0834 Expressions that are just numbers will be immediately converted rather than being evaluated 2023-06-18 21:11:14 +02:00
Auxilor
c85b8c08c7 Merge remote-tracking branch 'origin/master' 2023-06-18 14:35:31 +02:00
Auxilor
a65ac0aa4f Updated to 6.65.0 2023-06-18 14:23:18 +02:00
Auxilor
9d03ba2bd2 Fixed deprecated use of placeholder methods 2023-06-18 14:14:52 +02:00
Auxilor
8eb62d65b4 Fixed PersistentDataKeyType.BIG_DECIMAL 2023-06-18 14:11:50 +02:00
Will FP
178f21b1f3 Create CODEOWNERS 2023-06-17 21:14:57 +02:00
Will FP
a3fe4f97ff Merge pull request #280 from Auxilor/develop
Fixed not player messages
2023-06-17 21:09:31 +02:00
Auxilor
ff356fcde5 Fixed not player messages 2023-06-17 20:57:36 +02:00
Will FP
59b57b17ba Create master-pr.yml 2023-06-17 20:35:49 +02:00
Auxilor
c8f710161d Merge remote-tracking branch 'origin/master'
# Conflicts:
#	gradle.properties
2023-06-13 15:29:12 +02:00
Auxilor
f840a55734 Fixed 1.20 bugs 2023-06-13 15:28:12 +02:00
Auxilor
36677fe503 Updated to 6.63.1 2023-06-08 21:30:26 +01:00
Auxilor
7e74223352 Added 1.20 support 2023-06-08 21:30:16 +01:00
Auxilor
b6f2b9d4ea Added global %player% placeholder 2023-06-05 14:09:17 +01:00
Auxilor
f320e77008 Updated to 6.64.0 2023-06-05 13:44:50 +01:00
Auxilor
c8b255b358 Added placeholder extension methods 2023-06-05 13:44:42 +01:00
Auxilor
5b5e161062 Added head arg parser 2023-06-05 13:22:21 +01:00
Auxilor
ffa511176f Improved packet event error logging 2023-06-03 15:39:38 +01:00
Auxilor
8515ff2b7b Updated to 6.63.0 2023-06-03 15:13:51 +01:00
Auxilor
74aeaec257 Added NumberUtils.evaluateExpressionOrNull 2023-06-03 15:13:42 +01:00
Auxilor
0ce3d294d1 Updated to 6.62.2 2023-06-03 14:23:33 +01:00
Auxilor
7c7052f5b9 Placeholders with identical patterns will now override previous registrations 2023-06-03 14:23:15 +01:00
Auxilor
a6b7dda82d Added check to prevent !!float in configs 2023-06-03 14:20:59 +01:00
58 changed files with 1005 additions and 118 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @WillFP

21
.github/workflows/master-pr.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: PR Alert for Master Branch
on:
pull_request:
branches:
- master
jobs:
alert:
runs-on: ubuntu-latest
steps:
- name: Comment PR
uses: actions/github-script@v5
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '⚠️ PRs should not be opened against the `master` branch directly. Please use the `develop` branch as the base for your PRs. ⚠️'
})

View File

@@ -10,7 +10,7 @@ buildscript {
plugins { plugins {
id("java-library") id("java-library")
id("com.github.johnrengelman.shadow") version "7.1.2" id("com.github.johnrengelman.shadow") version "8.1.1"
id("maven-publish") id("maven-publish")
id("java") id("java")
kotlin("jvm") version "1.7.10" kotlin("jvm") version "1.7.10"
@@ -27,6 +27,7 @@ dependencies {
implementation(project(path = ":eco-core:core-nms:v1_19_R1", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_19_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_19_R2", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_19_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_19_R3", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_19_R3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_R1", configuration = "reobf"))
} }
allprojects { allprojects {
@@ -80,9 +81,6 @@ allprojects {
// LibsDisguises // LibsDisguises
maven("https://repo.md-5.net/content/groups/public/") maven("https://repo.md-5.net/content/groups/public/")
// UltraEconomy
maven("https://repo.techscode.com/repository/maven-releases/")
// PlayerPoints // PlayerPoints
maven("https://repo.rosewooddev.io/repository/public/") maven("https://repo.rosewooddev.io/repository/public/")

View File

@@ -527,9 +527,10 @@ public interface Eco {
* *
* @param expression The expression. * @param expression The expression.
* @param context The context. * @param context The context.
* @return The value of the expression, or zero if invalid. * @return The value of the expression, or null if invalid.
*/ */
double evaluate(@NotNull String expression, @Nullable
Double evaluate(@NotNull String expression,
@NotNull PlaceholderContext context); @NotNull PlaceholderContext context);
/** /**

View File

@@ -37,11 +37,19 @@ public class Prerequisite {
"Requires server to have ProtocolLib" "Requires server to have ProtocolLib"
); );
/**
* Requires the server to be running 1.20.
*/
public static final Prerequisite HAS_1_20 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("20"),
"Requires server to be running 1.20+"
);
/** /**
* Requires the server to be running 1.19.4. * Requires the server to be running 1.19.4.
*/ */
public static final Prerequisite HAS_1_19_4 = new Prerequisite( public static final Prerequisite HAS_1_19_4 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("19_R3"), () -> ProxyConstants.NMS_VERSION.contains("19_R3") || HAS_1_20.isMet(),
"Requires server to be running 1.19.4+" "Requires server to be running 1.19.4+"
); );
@@ -49,7 +57,7 @@ public class Prerequisite {
* Requires the server to be running 1.19. * Requires the server to be running 1.19.
*/ */
public static final Prerequisite HAS_1_19 = new Prerequisite( public static final Prerequisite HAS_1_19 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("19"), () -> ProxyConstants.NMS_VERSION.contains("19") || HAS_1_20.isMet(),
"Requires server to be running 1.19+" "Requires server to be running 1.19+"
); );

View File

@@ -15,6 +15,7 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -596,7 +597,10 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*/ */
default double getDoubleFromExpression(@NotNull String path, default double getDoubleFromExpression(@NotNull String path,
@NotNull PlaceholderContext context) { @NotNull PlaceholderContext context) {
return NumberUtils.evaluateExpression(this.getString(path), context.withInjectableContext(this)); return Objects.requireNonNullElseGet(
this.getDoubleOrNull(path),
() -> NumberUtils.evaluateExpression(this.getString(path), context.withInjectableContext(this))
);
} }
/** /**
@@ -648,6 +652,32 @@ public interface Config extends Cloneable, PlaceholderInjectable {
@Nullable @Nullable
List<? extends Config> getSubsectionsOrNull(@NotNull String path); List<? extends Config> getSubsectionsOrNull(@NotNull String path);
/**
* Get a big decimal from config.
*
* @param path The key to fetch the value from.
* @return The found value, or 0 if not found.
*/
@NotNull
default BigDecimal getBigDecimal(@NotNull final String path) {
return Objects.requireNonNullElse(getBigDecimalOrNull(path), BigDecimal.ZERO);
}
/**
* Get a big decimal from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
default BigDecimal getBigDecimalOrNull(@NotNull final String path) {
if (this.has(path)) {
return new BigDecimal(this.getString(path));
} else {
return null;
}
}
/** /**
* Get config type. * Get config type.
* *

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.display; package com.willfp.eco.core.display;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.fast.FastItemStack; import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.integrations.guidetection.GUIDetectionManager; import com.willfp.eco.core.integrations.guidetection.GUIDetectionManager;
import com.willfp.eco.util.NamespacedKeyUtils; import com.willfp.eco.util.NamespacedKeyUtils;
@@ -68,8 +69,10 @@ public final class Display {
Display.revert(itemStack); Display.revert(itemStack);
if (!itemStack.hasItemMeta()) { if (!Eco.get().getEcoPlugin().getConfigYml().getBool("display-without-meta")) {
return itemStack; if (!itemStack.hasItemMeta()) {
return itemStack;
}
} }
ItemStack original = itemStack.clone(); ItemStack original = itemStack.clone();

View File

@@ -98,6 +98,7 @@ public final class PlaceholderManager {
// Storing as immutable set leads to slower times to register placeholders, but much // Storing as immutable set leads to slower times to register placeholders, but much
// faster times to access registrations. // faster times to access registrations.
Set<Placeholder> pluginPlaceholders = new HashSet<>(REGISTERED_PLACEHOLDERS.get(placeholder.getPlugin())); Set<Placeholder> pluginPlaceholders = new HashSet<>(REGISTERED_PLACEHOLDERS.get(placeholder.getPlugin()));
pluginPlaceholders.removeIf(p -> p.getPattern().equals(placeholder.getPattern()));
pluginPlaceholders.add(placeholder); pluginPlaceholders.add(placeholder);
REGISTERED_PLACEHOLDERS.put(placeholder.getPlugin(), ImmutableSet.copyOf(pluginPlaceholders)); REGISTERED_PLACEHOLDERS.put(placeholder.getPlugin(), ImmutableSet.copyOf(pluginPlaceholders));
} }
@@ -168,7 +169,15 @@ public final class PlaceholderManager {
public static String translatePlaceholders(@NotNull final String text, public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player, @Nullable final Player player,
@NotNull final PlaceholderInjectable context) { @NotNull final PlaceholderInjectable context) {
return translatePlaceholders(text, player, context, new ArrayList<>()); return translatePlaceholders(
text,
new PlaceholderContext(
player,
null,
context,
new ArrayList<>()
)
);
} }
/** /**

View File

@@ -23,7 +23,8 @@ public final class ProxyConstants {
"v1_18_R2", "v1_18_R2",
"v1_19_R1", "v1_19_R1",
"v1_19_R2", "v1_19_R2",
"v1_19_R3" "v1_19_R3",
"v1_20_R1"
); );
private ProxyConstants() { private ProxyConstants() {

View File

@@ -12,6 +12,7 @@ import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@@ -251,12 +252,15 @@ public final class NumberUtils {
@Nullable final Player player, @Nullable final Player player,
@Nullable final PlaceholderInjectable context, @Nullable final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) { @NotNull final Collection<AdditionalPlayer> additionalPlayers) {
return Eco.get().evaluate(expression, new PlaceholderContext( return evaluateExpression(
player, expression,
null, new PlaceholderContext(
context, player,
additionalPlayers null,
)); context,
additionalPlayers
)
);
} }
/** /**
@@ -283,6 +287,22 @@ public final class NumberUtils {
*/ */
public static double evaluateExpression(@NotNull final String expression, public static double evaluateExpression(@NotNull final String expression,
@NotNull final PlaceholderContext context) { @NotNull final PlaceholderContext context) {
return Objects.requireNonNullElse(
evaluateExpressionOrNull(expression, context),
0.0
);
}
/**
* Evaluate an expression in a context.
*
* @param expression The expression.
* @param context The context.
* @return The value of the expression, or zero if invalid.
*/
@Nullable
public static Double evaluateExpressionOrNull(@NotNull final String expression,
@NotNull final PlaceholderContext context) {
return Eco.get().evaluate(expression, context); return Eco.get().evaluate(expression, context);
} }

View File

@@ -0,0 +1,14 @@
@file:JvmName("PlaceholderExtensions")
package com.willfp.eco.core.placeholder
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.context.PlaceholderContext
/** @see PlaceholderManager.translatePlaceholders */
fun String.translatePlaceholders(context: PlaceholderContext) =
PlaceholderManager.translatePlaceholders(this, context)
/** @see PlaceholderManager.findPlaceholdersIn */
fun String.findPlaceholders(): List<String> =
PlaceholderManager.findPlaceholdersIn(this)

View File

@@ -31,3 +31,7 @@ fun evaluateExpression(expression: String) =
/** @see NumberUtils.evaluateExpression */ /** @see NumberUtils.evaluateExpression */
fun evaluateExpression(expression: String, context: PlaceholderContext) = fun evaluateExpression(expression: String, context: PlaceholderContext) =
NumberUtils.evaluateExpression(expression, context) NumberUtils.evaluateExpression(expression, context)
/** @see NumberUtils.evaluateExpressionOrNull */
fun evaluateExpressionOrNull(expression: String, context: PlaceholderContext) =
NumberUtils.evaluateExpressionOrNull(expression, context)

View File

@@ -6,7 +6,7 @@ dependencies {
implementation("org.reflections:reflections:0.9.12") implementation("org.reflections:reflections:0.9.12")
implementation("org.objenesis:objenesis:3.2") implementation("org.objenesis:objenesis:3.2")
compileOnly("org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT") compileOnly("org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.10.10") compileOnly("me.clip:placeholderapi:2.10.10")
compileOnly("net.kyori:adventure-text-minimessage:4.10.0") compileOnly("net.kyori:adventure-text-minimessage:4.10.0")
compileOnly("net.kyori:adventure-platform-bukkit:4.1.0") compileOnly("net.kyori:adventure-platform-bukkit:4.1.0")

View File

@@ -106,7 +106,7 @@ abstract class HandledCommand(
} }
try { try {
notifyFalse(!isPlayersOnly || sender is Player, LangYml.KEY_NOT_PLAYER) notifyFalse(!isPlayersOnly || sender is Player, "not-player")
onExecute(sender, args) onExecute(sender, args)

View File

@@ -30,6 +30,8 @@ internal fun Any?.constrainConfigTypes(type: ConfigType): Any? = when (this) {
this.toMutableList() this.toMutableList()
} }
} }
is Float -> this.toDouble() // Should prevent !!float from being written
else -> this else -> this
} }
@@ -110,6 +112,7 @@ private object YamlConfigTypeHandler : ConfigTypeHandler(ConfigType.YAML) {
representer.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK representer.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
return Yaml( return Yaml(
@Suppress("DEPRECATION")
YamlConstructor(), YamlConstructor(),
representer, representer,
yamlOptions, yamlOptions,

View File

@@ -16,11 +16,16 @@ open class EcoConfig(
private val values = ConcurrentHashMap<String, Any?>() private val values = ConcurrentHashMap<String, Any?>()
@Transient @Transient
var injections = ConcurrentHashMap<String, InjectablePlaceholder>() private val injections = mutableMapOf<String, InjectablePlaceholder>()
fun init(values: Map<String, Any?>) { @Transient
private var injectionHash = 0
fun init(values: Map<String, Any?>, injections: Map<String, InjectablePlaceholder>) {
this.values.clear() this.values.clear()
this.values.putAll(values.normalizeToConfig(this.type)) this.values.putAll(values.normalizeToConfig(this.type))
this.addInjectablePlaceholder(injections.values)
} }
override fun toPlaintext(): String { override fun toPlaintext(): String {
@@ -179,6 +184,7 @@ open class EcoConfig(
override fun addInjectablePlaceholder(placeholders: Iterable<InjectablePlaceholder>) { override fun addInjectablePlaceholder(placeholders: Iterable<InjectablePlaceholder>) {
for (placeholder in placeholders) { for (placeholder in placeholders) {
injections[placeholder.pattern.pattern()] = placeholder injections[placeholder.pattern.pattern()] = placeholder
injectionHash = injectionHash xor placeholder.hashCode()
} }
} }
@@ -188,6 +194,7 @@ open class EcoConfig(
override fun clearInjectedPlaceholders() { override fun clearInjectedPlaceholders() {
injections.clear() injections.clear()
injectionHash = 0 // Reset the hash
} }
override fun toMap(): MutableMap<String, Any?> { override fun toMap(): MutableMap<String, Any?> {
@@ -239,18 +246,6 @@ open class EcoConfig(
} }
override fun hashCode(): Int { override fun hashCode(): Int {
/*
The keys are completely redundant, as they are only used to prevent
duplicate keys in the map. Therefore, we can ignore them and just
hash the actual placeholder values.
*/
var injectionHash = 0
injections.forEachValue(5) {
injectionHash = injectionHash xor (it.hashCode() shl 5)
}
// hashCode() has to compute extremely quickly, so we're using bitwise, because why not? // hashCode() has to compute extremely quickly, so we're using bitwise, because why not?
// Fucking filthy to use identityHashCode here, but it should be extremely fast // Fucking filthy to use identityHashCode here, but it should be extremely fast
val identityHash = System.identityHashCode(this) val identityHash = System.identityHashCode(this)

View File

@@ -10,7 +10,6 @@ class EcoConfigSection(
injections: Map<String, InjectablePlaceholder> = emptyMap() injections: Map<String, InjectablePlaceholder> = emptyMap()
) : EcoConfig(type) { ) : EcoConfig(type) {
init { init {
this.init(values) this.init(values, injections)
this.injections = ConcurrentHashMap(injections)
} }
} }

View File

@@ -91,7 +91,7 @@ open class EcoLoadableConfig(
protected fun init(reader: Reader) { protected fun init(reader: Reader) {
val string = reader.readToString() val string = reader.readToString()
makeHeader(string) makeHeader(string)
super.init(type.toMap(string)) super.init(type.toMap(string), emptyMap())
} }
fun init(file: File) { fun init(file: File) {

View File

@@ -1,12 +1,12 @@
package com.willfp.eco.internal.config package com.willfp.eco.internal.config
import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.config.interfaces.Config
import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.nodes.Node import org.yaml.snakeyaml.nodes.Node
import org.yaml.snakeyaml.representer.Represent import org.yaml.snakeyaml.representer.Represent
import org.yaml.snakeyaml.representer.Representer import org.yaml.snakeyaml.representer.Representer
@Suppress("DEPRECATION") class EcoRepresenter : Representer(DumperOptions()) {
class EcoRepresenter : Representer() {
init { init {
multiRepresenters[Config::class.java] = RepresentConfig(multiRepresenters[Map::class.java]!!) multiRepresenters[Config::class.java] = RepresentConfig(multiRepresenters[Map::class.java]!!)
} }

View File

@@ -50,7 +50,7 @@ open class EcoUpdatableConfig(
val reader = BufferedReader(InputStreamReader(newIn, StandardCharsets.UTF_8)) val reader = BufferedReader(InputStreamReader(newIn, StandardCharsets.UTF_8))
val config = EcoConfigSection(type, emptyMap()) val config = EcoConfigSection(type, emptyMap())
config.init(type.toMap(reader.readToString())) config.init(type.toMap(reader.readToString()), emptyMap())
return config return config
} }

View File

@@ -24,8 +24,8 @@ fun PacketEvent.handleSend() {
listener.listener.onSend(this) listener.listener.onSend(this)
} catch (e: Exception) { } catch (e: Exception) {
listener.plugin.logger.warning( listener.plugin.logger.warning(
"Exception in packet listener ${listener.listener.javaClass.simpleName}" + "Exception in packet listener ${listener.listener.javaClass.name}" +
" for packet ${packet.javaClass.simpleName}!" " for packet ${packet.handle.javaClass.name}!"
) )
e.printStackTrace() e.printStackTrace()
} }
@@ -40,8 +40,8 @@ fun PacketEvent.handleReceive() {
listener.listener.onReceive(this) listener.listener.onReceive(this)
} catch (e: Exception) { } catch (e: Exception) {
listener.plugin.logger.warning( listener.plugin.logger.warning(
"Exception in packet listener ${listener.listener.javaClass.simpleName}" + "Exception in packet listener ${listener.listener.javaClass.name}" +
" for packet ${packet.javaClass.simpleName}!" " for packet ${packet.handle.javaClass.name}!"
) )
e.printStackTrace() e.printStackTrace()
} }

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.internal.items
import com.willfp.eco.core.items.args.LookupArgParser
import org.bukkit.Bukkit
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.inventory.meta.SkullMeta
import java.util.function.Predicate
object ArgParserHead : LookupArgParser {
override fun parseArguments(args: Array<out String>, meta: ItemMeta): Predicate<ItemStack>? {
if (meta !is SkullMeta) {
return null
}
var playerName: String? = null
for (arg in args) {
val argSplit = arg.split(":")
if (!argSplit[0].equals("head", ignoreCase = true)) {
continue
}
if (argSplit.size < 2) {
continue
}
playerName = argSplit[1]
}
playerName ?: return null
@Suppress("DEPRECATION")
val player = Bukkit.getOfflinePlayer(playerName)
meta.owningPlayer = player
return Predicate {
val testMeta = it.itemMeta as? SkullMeta ?: return@Predicate false
testMeta.owningPlayer?.uniqueId == player.uniqueId
}
}
override fun serializeBack(meta: ItemMeta): String? {
if (meta !is SkullMeta) {
return null
}
if (meta.owningPlayer == null) {
return null
}
if (meta.owningPlayer!!.name.equals("null", true) || meta.owningPlayer!!.name == null) {
return null
}
return "head:${meta.owningPlayer?.name}"
}
}

View File

@@ -0,0 +1,50 @@
package com.willfp.eco.internal.items
import com.willfp.eco.core.items.args.LookupArgParser
import org.bukkit.Color
import org.bukkit.NamespacedKey
import org.bukkit.Registry
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ArmorMeta
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.inventory.meta.LeatherArmorMeta
import org.bukkit.inventory.meta.trim.ArmorTrim
import org.bukkit.inventory.meta.trim.TrimMaterial
import org.bukkit.inventory.meta.trim.TrimPattern
import java.util.function.Predicate
object ArgParserTrim : LookupArgParser {
override fun parseArguments(args: Array<out String>, meta: ItemMeta): Predicate<ItemStack>? {
if (meta !is ArmorMeta) return null
var material: TrimMaterial? = null
var pattern: TrimPattern? = null
for (arg in args) {
val argSplit = arg.split(":")
if (!argSplit[0].equals("trim", ignoreCase = true)) {
continue
}
material = Registry.TRIM_MATERIAL.get(NamespacedKey.minecraft(argSplit.getOrElse(1) {""}))
pattern = Registry.TRIM_PATTERN.get(NamespacedKey.minecraft(argSplit.getOrElse(2) {""}))
}
if (material == null || pattern == null) return null
meta.trim = ArmorTrim(material, pattern)
return Predicate {
val testMeta = it.itemMeta as? ArmorMeta ?: return@Predicate false
val trim = testMeta.trim ?: return@Predicate false
return@Predicate trim.material == material
&& trim.pattern == pattern
}
}
override fun serializeBack(meta: ItemMeta): String? {
val trim = (meta as? ArmorMeta)?.trim ?: return null
return "trim:${trim.material.key.key.lowercase()}:${trim.pattern.key.key.lowercase()}"
}
}

View File

@@ -4,8 +4,23 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.InjectablePlaceholder import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.Placeholder import com.willfp.eco.core.placeholder.Placeholder
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.templates.SimpleInjectablePlaceholder
import java.util.regex.Pattern import java.util.regex.Pattern
/*
A set of global placeholders that are always available.
*/
private val globalPlaceholders = setOf<Placeholder>(
object : SimpleInjectablePlaceholder("player") {
override fun getValue(args: String, context: PlaceholderContext): String? {
return context.player?.name
}
},
)
class PlaceholderLookup( class PlaceholderLookup(
val args: String, val args: String,
val plugin: EcoPlugin?, val plugin: EcoPlugin?,
@@ -29,6 +44,12 @@ class PlaceholderLookup(
} }
} }
for (placeholder in globalPlaceholders) {
if (placeholder.matches(this)) {
return placeholder
}
}
return null return null
} }

View File

@@ -7,7 +7,6 @@ import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.Placeholder import com.willfp.eco.core.placeholder.Placeholder
import com.willfp.eco.core.placeholder.context.PlaceholderContext import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.util.StringUtils import com.willfp.eco.util.StringUtils
import java.util.Optional
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/* /*
@@ -23,7 +22,7 @@ class PlaceholderParser {
private val placeholderLookupCache = Caffeine.newBuilder() private val placeholderLookupCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS) .expireAfterWrite(1, TimeUnit.SECONDS)
.build<PlaceholderLookup, Optional<Placeholder>>() .build<PlaceholderLookup, Placeholder?>()
fun translatePlacholders(text: String, context: PlaceholderContext): String { fun translatePlacholders(text: String, context: PlaceholderContext): String {
return translatePlacholders(text, context, context.injectableContext.placeholderInjections) return translatePlacholders(text, context, context.injectableContext.placeholderInjections)
@@ -122,10 +121,10 @@ class PlaceholderParser {
val lookup = PlaceholderLookup(args, plugin, injections) val lookup = PlaceholderLookup(args, plugin, injections)
val placeholder = placeholderLookupCache.get(lookup) { val placeholder = placeholderLookupCache.get(lookup) {
Optional.ofNullable(it.findMatchingPlaceholder()) it.findMatchingPlaceholder()
}.orElse(null) ?: return null }
return placeholder.getValue(args, context) return placeholder?.getValue(args, context)
} }
private fun translateEcoPlaceholdersIn( private fun translateEcoPlaceholdersIn(

View File

@@ -0,0 +1,39 @@
plugins {
id("io.papermc.paperweight.userdev")
}
group = "com.willfp"
version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:nms-common"))
paperweight.paperDevBundle("1.20-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-text-minimessage:4.11.0") {
version {
strictly("4.11.0")
}
exclude(group = "net.kyori", module = "adventure-api")
}
}
tasks {
build {
dependsOn(reobfJar)
}
reobfJar {
mustRunAfter(shadowJar)
}
shadowJar {
relocate(
"com.willfp.eco.internal.spigot.proxy.common",
"com.willfp.eco.internal.spigot.proxy.v1_20_R1.common"
)
relocate(
"net.kyori.adventure.text.minimessage",
"com.willfp.eco.internal.spigot.proxy.v1_20_R1.minimessage"
)
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.v1_20_R1.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -0,0 +1,159 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_20_R1.CraftServer
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftMob
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_20_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_20_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.craftbukkit.v1_20_R1.util.CraftMagicNumbers
import org.bukkit.craftbukkit.v1_20_R1.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
private val cisHandle: Field = CraftItemStack::class.java.getDeclaredField("handle").apply {
isAccessible = true
}
private val pdcRegsitry = Class.forName("org.bukkit.craftbukkit.v1_20_R1.inventory.CraftMetaItem")
.getDeclaredField("DATA_TYPE_REGISTRY")
.apply { isAccessible = true }
.get(null) as CraftPersistentDataTypeRegistry
override val nbtTagString = CraftMagicNumbers.NBT.TAG_STRING
override fun toPathfinderMob(mob: Mob): PathfinderMob? {
val craft = mob as? CraftMob ?: return null
return craft.handle as? PathfinderMob
}
override fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation =
CraftNamespacedKey.toMinecraft(namespacedKey)
override fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack {
return if (itemStack !is CraftItemStack) {
CraftItemStack.asNMSCopy(itemStack)
} else {
cisHandle[itemStack] as net.minecraft.world.item.ItemStack? ?: CraftItemStack.asNMSCopy(itemStack)
}
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
if (itemStack !is CraftItemStack) {
itemStack.itemMeta = CraftItemStack.asCraftMirror(nmsStack).itemMeta
}
}
override fun toBukkitEntity(entity: net.minecraft.world.entity.LivingEntity): LivingEntity? =
CraftEntity.getEntity(Bukkit.getServer() as CraftServer, entity) as? LivingEntity
override fun makePdc(tag: CompoundTag, base: Boolean): PersistentDataContainer {
fun emptyPdc(): CraftPersistentDataContainer = CraftPersistentDataContainer(pdcRegsitry)
fun CompoundTag?.toPdc(): PersistentDataContainer {
val pdc = emptyPdc()
this ?: return pdc
val keys = this.allKeys
for (key in keys) {
pdc.put(key, this[key])
}
return pdc
}
return if (base) {
tag.toPdc()
} else {
if (tag.contains("PublicBukkitValues")) {
tag.getCompound("PublicBukkitValues").toPdc()
} else {
emptyPdc()
}
}
}
override fun setPdc(
tag: CompoundTag,
pdc: PersistentDataContainer?,
item: net.minecraft.world.item.ItemStack?
) {
fun CraftPersistentDataContainer.toTag(): CompoundTag {
val compound = CompoundTag()
val rawPublicMap: Map<String, Tag> = this.raw
for ((key, value) in rawPublicMap) {
compound.put(key, value)
}
return compound
}
val container = when (pdc) {
is CraftPersistentDataContainer? -> pdc
else -> null
}
if (item != null) {
if (container != null && !container.isEmpty) {
for (key in tag.allKeys.toSet()) {
tag.remove(key)
}
tag.merge(container.toTag())
} else {
item.tag = null
}
} else {
if (container != null && !container.isEmpty) {
tag.put("PublicBukkitValues", container.toTag())
} else {
tag.remove("PublicBukkitValues")
}
}
}
override fun materialToItem(material: Material): Item =
BuiltInRegistries.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(BuiltInRegistries.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
}
}

View File

@@ -0,0 +1,16 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.internal.entities.EcoDummyEntity
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import org.bukkit.Location
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld
import org.bukkit.entity.Entity
import org.bukkit.entity.EntityType
class DummyEntityFactory : DummyEntityFactoryProxy {
override fun createDummyEntity(location: Location): Entity {
val world = location.world as CraftWorld
@Suppress("UsePropertyAccessSyntax")
return EcoDummyEntity(world.createEntity(location, EntityType.ZOMBIE.entityClass).getBukkitEntity())
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.v1_20_R1.entity.EcoEntityController
import org.bukkit.entity.Mob
class EntityControllerFactory : EntityControllerFactoryProxy {
override fun <T : Mob> createEntityController(entity: T): EntityController<T> {
return EcoEntityController(entity)
}
}

View File

@@ -0,0 +1,82 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_20_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_20_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc)
else -> throw IllegalArgumentException("Custom PDC instance ims not supported!")
}
}
override fun newPdc(): PersistentDataContainer {
return CraftPersistentDataContainer(registry)
}
inner class EcoPersistentDataContainer(
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =
CraftPersistentDataContainer::class.java.getDeclaredField("customDataTags")
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] =
registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {
val value = customDataTags[key] ?: return false
return registry.isInstanceOf(dataType.primitiveType, value)
}
override fun <T : Any, Z : Any> get(key: String, dataType: PersistentDataType<T, Z>): Z? {
val value = customDataTags[key] ?: return null
return dataType.fromPrimitive(registry.extract(dataType.primitiveType, value), handle.adapterContext)
}
override fun <T : Any, Z : Any> getOrDefault(
key: String,
dataType: PersistentDataType<T, Z>,
defaultValue: Z
): Z {
return get(key, dataType) ?: defaultValue
}
override fun remove(key: String) {
customDataTags.remove(key)
}
override fun getAllKeys(): MutableSet<String> {
return customDataTags.keys
}
override fun getBase(): PersistentDataContainer {
return handle
}
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.item.EcoFastItemStack
import org.bukkit.inventory.ItemStack
class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack {
return EcoFastItemStack(itemStack)
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.minimessage.MiniMessage
class MiniMessageTranslator : MiniMessageTranslatorProxy {
override fun format(message: String): String {
var mut = message
val startsWithPrefix = mut.startsWith(Display.PREFIX)
if (startsWithPrefix) {
mut = mut.substring(2)
}
mut = mut.replace('§', '&')
val miniMessage = runCatching {
MiniMessage.miniMessage().deserialize(
mut
).toLegacy()
}.getOrNull() ?: mut
mut = if (startsWithPrefix) {
Display.PREFIX + miniMessage
} else {
miniMessage
}
return mut
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketAutoRecipe
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetCreativeSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler : PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
return listOf(
PacketAutoRecipe(plugin),
PacketHeldItemSlot,
PacketOpenWindowMerchant,
PacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin)
)
}
}

View File

@@ -0,0 +1,52 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack
import org.bukkit.inventory.ItemStack
class SNBTConverter : SNBTConverterProxy {
override fun fromSNBT(snbt: String): ItemStack? {
val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return null
val nms = net.minecraft.world.item.ItemStack.of(nbt)
return CraftItemStack.asBukkitCopy(nms)
}
override fun toSNBT(itemStack: ItemStack): String {
val nms = CraftItemStack.asNMSCopy(itemStack)
return SnbtPrinterTagVisitor().visit(nms.save(CompoundTag()))
}
override fun makeSNBTTestable(snbt: String): TestableItem {
val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return EmptyTestableItem()
val nms = net.minecraft.world.item.ItemStack.of(nbt)
if (nms == net.minecraft.world.item.ItemStack.EMPTY) {
return EmptyTestableItem()
}
nbt.remove("Count")
return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), nbt)
}
class SNBTTestableItem(
private val item: ItemStack,
private val tag: CompoundTag
) : TestableItem {
override fun matches(itemStack: ItemStack?): Boolean {
if (itemStack == null) {
return false
}
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.texture
import org.bukkit.inventory.meta.SkullMeta
class Skull : SkullProxy {
override fun setSkullTexture(
meta: SkullMeta,
base64: String
) {
meta.texture = base64
}
override fun getSkullTexture(
meta: SkullMeta
): String? = meta.texture
}

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_20_R1.CraftServer
class TPS : TPSProxy {
override fun getTPS(): Double {
return (Bukkit.getServer() as CraftServer).handle.server.recentTps[0]
}
}

View File

@@ -0,0 +1,95 @@
package com.willfp.eco.internal.spigot.proxy.v1_20_R1.entity
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.ai.CustomGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.getGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.toPathfinderMob
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import org.bukkit.entity.Mob
class EcoEntityController<T : Mob>(
private val handle: T
) : EntityController<T> {
override fun addEntityGoal(priority: Int, goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.addGoal(
priority,
goal.getGoalFactory()?.create(goal, nms) ?: return this
)
return this
}
override fun removeEntityGoal(goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.goalSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.goalSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearEntityGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.availableGoals.clear()
return this
}
override fun addTargetGoal(priority: Int, goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.addGoal(
priority, goal.getGoalFactory()?.create(goal, nms) ?: return this
)
nms.targetSelector
return this
}
override fun removeTargetGoal(goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.targetSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.targetSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearTargetGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.availableGoals.clear()
return this
}
private fun getNms(): PathfinderMob? {
return handle.toPathfinderMob()
}
override fun getEntity(): T {
return handle
}
}

View File

@@ -13,7 +13,7 @@ dependencies {
implementation("org.jetbrains.exposed:exposed-jdbc:0.37.3") implementation("org.jetbrains.exposed:exposed-jdbc:0.37.3")
implementation("com.zaxxer:HikariCP:5.0.0") implementation("com.zaxxer:HikariCP:5.0.0")
implementation("net.kyori:adventure-platform-bukkit:4.1.0") implementation("net.kyori:adventure-platform-bukkit:4.1.0")
implementation("org.javassist:javassist:3.28.0-GA") implementation("org.javassist:javassist:3.29.2-GA")
implementation("org.mongodb:mongodb-driver-sync:4.6.0") implementation("org.mongodb:mongodb-driver-sync:4.6.0")
implementation("org.litote.kmongo:kmongo-coroutine:4.6.0") implementation("org.litote.kmongo:kmongo-coroutine:4.6.0")
implementation("com.moandjiezana.toml:toml4j:0.7.2") { implementation("com.moandjiezana.toml:toml4j:0.7.2") {
@@ -40,7 +40,7 @@ dependencies {
compileOnly("com.github.oraxen:oraxen:1.155.0") compileOnly("com.github.oraxen:oraxen:1.155.0")
compileOnly("com.github.brcdev-minecraft:shopgui-api:3.0.0") compileOnly("com.github.brcdev-minecraft:shopgui-api:3.0.0")
compileOnly("com.github.LoneDev6:API-ItemsAdder:2.4.7") compileOnly("com.github.LoneDev6:API-ItemsAdder:2.4.7")
compileOnly("com.arcaniax:HeadDatabase-API:1.3.0") compileOnly("com.arcaniax:HeadDatabase-API:1.3.1")
compileOnly("com.gmail.filoghost.holographicdisplays:holographicdisplays-api:2.4.0") compileOnly("com.gmail.filoghost.holographicdisplays:holographicdisplays-api:2.4.0")
compileOnly("com.github.EssentialsX:Essentials:2.18.2") compileOnly("com.github.EssentialsX:Essentials:2.18.2")
compileOnly("com.bgsoftware:SuperiorSkyblockAPI:1.8.3") compileOnly("com.bgsoftware:SuperiorSkyblockAPI:1.8.3")
@@ -51,10 +51,9 @@ dependencies {
compileOnly("com.github.Gypopo:EconomyShopGUI-API:1.4.6") compileOnly("com.github.Gypopo:EconomyShopGUI-API:1.4.6")
compileOnly("com.github.N0RSKA:ScytherAPI:55a") compileOnly("com.github.N0RSKA:ScytherAPI:55a")
compileOnly("com.ticxo.modelengine:api:R3.0.1") compileOnly("com.ticxo.modelengine:api:R3.0.1")
compileOnly("me.TechsCode:UltraEconomyAPI:1.0.0")
compileOnly("org.black_ixx:playerpoints:3.2.5") compileOnly("org.black_ixx:playerpoints:3.2.5")
compileOnly("com.github.Ssomar-Developement:SCore:3.4.7") compileOnly("com.github.Ssomar-Developement:SCore:3.4.7")
compileOnly("io.lumine:Mythic:5.2.1") compileOnly("io.lumine:Mythic:5.3.5")
compileOnly("io.lumine:LumineUtils:1.19-SNAPSHOT") compileOnly("io.lumine:LumineUtils:1.19-SNAPSHOT")
compileOnly("com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT") compileOnly("com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT")
compileOnly("com.github.sirblobman.combatlogx:api:11.0.0.0-SNAPSHOT") compileOnly("com.github.sirblobman.combatlogx:api:11.0.0.0-SNAPSHOT")

View File

@@ -45,8 +45,10 @@ import com.willfp.eco.internal.items.ArgParserColor
import com.willfp.eco.internal.items.ArgParserCustomModelData import com.willfp.eco.internal.items.ArgParserCustomModelData
import com.willfp.eco.internal.items.ArgParserEnchantment import com.willfp.eco.internal.items.ArgParserEnchantment
import com.willfp.eco.internal.items.ArgParserFlag import com.willfp.eco.internal.items.ArgParserFlag
import com.willfp.eco.internal.items.ArgParserHead
import com.willfp.eco.internal.items.ArgParserName import com.willfp.eco.internal.items.ArgParserName
import com.willfp.eco.internal.items.ArgParserTexture import com.willfp.eco.internal.items.ArgParserTexture
import com.willfp.eco.internal.items.ArgParserTrim
import com.willfp.eco.internal.items.ArgParserUnbreakable import com.willfp.eco.internal.items.ArgParserUnbreakable
import com.willfp.eco.internal.lookup.SegmentParserGroup import com.willfp.eco.internal.lookup.SegmentParserGroup
import com.willfp.eco.internal.lookup.SegmentParserUseIfPresent import com.willfp.eco.internal.lookup.SegmentParserUseIfPresent
@@ -61,11 +63,7 @@ import com.willfp.eco.internal.spigot.data.PlayerBlockListener
import com.willfp.eco.internal.spigot.data.ProfileHandler import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.storage.ProfileSaver import com.willfp.eco.internal.spigot.data.storage.ProfileSaver
import com.willfp.eco.internal.spigot.drops.CollatedRunnable import com.willfp.eco.internal.spigot.drops.CollatedRunnable
import com.willfp.eco.internal.spigot.eventlisteners.EntityDeathByEntityListeners import com.willfp.eco.internal.spigot.eventlisteners.*
import com.willfp.eco.internal.spigot.eventlisteners.NaturalExpGainListenersPaper
import com.willfp.eco.internal.spigot.eventlisteners.NaturalExpGainListenersSpigot
import com.willfp.eco.internal.spigot.eventlisteners.PlayerJumpListenersPaper
import com.willfp.eco.internal.spigot.eventlisteners.PlayerJumpListenersSpigot
import com.willfp.eco.internal.spigot.eventlisteners.armor.ArmorChangeEventListeners import com.willfp.eco.internal.spigot.eventlisteners.armor.ArmorChangeEventListeners
import com.willfp.eco.internal.spigot.eventlisteners.armor.ArmorListener import com.willfp.eco.internal.spigot.eventlisteners.armor.ArmorListener
import com.willfp.eco.internal.spigot.gui.GUIListener import com.willfp.eco.internal.spigot.gui.GUIListener
@@ -150,6 +148,10 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
Items.registerArgParser(ArgParserFlag) Items.registerArgParser(ArgParserFlag)
Items.registerArgParser(ArgParserUnbreakable) Items.registerArgParser(ArgParserUnbreakable)
Items.registerArgParser(ArgParserName) Items.registerArgParser(ArgParserName)
Items.registerArgParser(ArgParserHead)
if (Prerequisite.HAS_1_20.isMet) {
Items.registerArgParser(ArgParserTrim)
}
Entities.registerArgParser(EntityArgParserName) Entities.registerArgParser(EntityArgParserName)
Entities.registerArgParser(EntityArgParserNoAI) Entities.registerArgParser(EntityArgParserNoAI)
@@ -388,7 +390,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
val listeners = mutableListOf( val listeners = mutableListOf(
ArmorListener(), ArmorListener(),
EntityDeathByEntityListeners(this), EntityDeathByEntityListeners(this),
CraftingRecipeListener(), CraftingRecipeListener(this),
StackedRecipeListener(this), StackedRecipeListener(this),
GUIListener(this), GUIListener(this),
ArrowDataListener(this), ArrowDataListener(this),

View File

@@ -21,7 +21,6 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import java.math.BigDecimal
import java.util.UUID import java.util.UUID
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -85,8 +84,7 @@ class MySQLDataHandler(
PersistentDataKeyType.BOOLEAN -> data.getBoolOrNull(key.key.toString()) PersistentDataKeyType.BOOLEAN -> data.getBoolOrNull(key.key.toString())
PersistentDataKeyType.STRING_LIST -> data.getStringsOrNull(key.key.toString()) PersistentDataKeyType.STRING_LIST -> data.getStringsOrNull(key.key.toString())
PersistentDataKeyType.CONFIG -> data.getSubsectionOrNull(key.key.toString()) PersistentDataKeyType.CONFIG -> data.getSubsectionOrNull(key.key.toString())
PersistentDataKeyType.BIG_DECIMAL -> if (data.has(key.key.toString())) PersistentDataKeyType.BIG_DECIMAL -> data.getBigDecimalOrNull(key.key.toString())
BigDecimal(data.getString(key.key.toString())) else null
else -> null else -> null
} }

View File

@@ -5,7 +5,6 @@ import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.internal.spigot.EcoSpigotPlugin import com.willfp.eco.internal.spigot.EcoSpigotPlugin
import com.willfp.eco.internal.spigot.data.ProfileHandler import com.willfp.eco.internal.spigot.data.ProfileHandler
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import java.math.BigDecimal
import java.util.UUID import java.util.UUID
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -28,8 +27,7 @@ class YamlDataHandler(
PersistentDataKeyType.BOOLEAN -> dataYml.getBoolOrNull("player.$uuid.${key.key}") as T? PersistentDataKeyType.BOOLEAN -> dataYml.getBoolOrNull("player.$uuid.${key.key}") as T?
PersistentDataKeyType.STRING_LIST -> dataYml.getStringsOrNull("player.$uuid.${key.key}") as T? PersistentDataKeyType.STRING_LIST -> dataYml.getStringsOrNull("player.$uuid.${key.key}") as T?
PersistentDataKeyType.CONFIG -> dataYml.getSubsectionOrNull("player.$uuid.${key.key}") as T? PersistentDataKeyType.CONFIG -> dataYml.getSubsectionOrNull("player.$uuid.${key.key}") as T?
PersistentDataKeyType.BIG_DECIMAL -> (if (dataYml.has(key.key.toString())) PersistentDataKeyType.BIG_DECIMAL -> dataYml.getBigDecimalOrNull("player.$uuid.${key.key}") as T?
BigDecimal(dataYml.getString(key.key.toString())) else null) as T?
else -> null else -> null
} }

View File

@@ -15,6 +15,7 @@ import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.inventory.ClickType import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryAction
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryDragEvent import org.bukkit.event.inventory.InventoryDragEvent
@@ -216,6 +217,17 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
} }
} }
@EventHandler
fun temporaryExploitPatch(event: InventoryClickEvent) {
if (event.player.renderedInventory == null) {
return
}
if (event.action == InventoryAction.COLLECT_TO_CURSOR) {
event.isCancelled = true
}
}
private fun Player.renderActiveMenu() { private fun Player.renderActiveMenu() {
val rendered = this.renderedInventory ?: return val rendered = this.renderedInventory ?: return

View File

@@ -6,11 +6,11 @@ import org.bukkit.block.Block
import org.bukkit.entity.LivingEntity import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.kingdoms.constants.group.Kingdom import org.kingdoms.constants.group.Kingdom
import org.kingdoms.constants.group.model.relationships.StandardRelationAttribute
import org.kingdoms.constants.land.Land import org.kingdoms.constants.land.Land
import org.kingdoms.constants.land.location.SimpleChunkLocation
import org.kingdoms.constants.player.KingdomPlayer import org.kingdoms.constants.player.KingdomPlayer
import org.kingdoms.constants.player.StandardKingdomPermission
import org.kingdoms.managers.PvPManager import org.kingdoms.managers.PvPManager
import org.kingdoms.managers.land.BuildingProcessor
class AntigriefKingdoms : AntigriefIntegration { class AntigriefKingdoms : AntigriefIntegration {
override fun canBreakBlock( override fun canBreakBlock(
@@ -21,34 +21,35 @@ class AntigriefKingdoms : AntigriefIntegration {
if (kp.isAdmin) { if (kp.isAdmin) {
return true return true
} }
val kingdom: Kingdom = kp.kingdom ?: return false return BuildingProcessor(
val land = Land.getLand(block) ?: return true player,
val permission = if (land.isNexusLand) { kp,
StandardKingdomPermission.NEXUS_BUILD SimpleChunkLocation.of(block),
} else StandardKingdomPermission.BUILD
return if (!kp.hasPermission(permission)) {
false false
} else kingdom.hasAttribute(land.kingdom, StandardRelationAttribute.BUILD) ).process().isSuccessful
} }
override fun canCreateExplosion( override fun canCreateExplosion(
player: Player, player: Player,
location: Location location: Location
): Boolean { ): Boolean {
val land = Land.getLand(location) ?: return true return canBreakBlock(player, location.block)
if (!land.isClaimed) {
return true
}
val kingdom: Kingdom = land.kingdom
return kingdom.isMember(player)
} }
override fun canPlaceBlock( override fun canPlaceBlock(
player: Player, player: Player,
block: Block block: Block
): Boolean { ): Boolean {
return canBreakBlock(player, block) val kp: KingdomPlayer = KingdomPlayer.getKingdomPlayer(player)
if (kp.isAdmin) {
return true
}
return BuildingProcessor(
player,
kp,
SimpleChunkLocation.of(block),
true
).process().isSuccessful
} }
override fun canInjure( override fun canInjure(

View File

@@ -17,7 +17,7 @@ class CustomEntitiesMythicMobs : CustomEntitiesIntegration {
CustomEntity( CustomEntity(
key, key,
{ {
val entityId = api.getMythicMobInstance(it)?.type?.entityType?.name ?: return@CustomEntity false val entityId = api.getMythicMobInstance(it)?.type?.internalName ?: return@CustomEntity false
entityId.equals(id, ignoreCase = true) entityId.equals(id, ignoreCase = true)
}, },
{ {

View File

@@ -32,15 +32,15 @@ class PriceFactoryUltraEconomy(private val currency: Currency) : PriceFactory {
get() = api.accounts.uuid(this.uniqueId).orElse(null) get() = api.accounts.uuid(this.uniqueId).orElse(null)
override fun canAfford(player: Player, multiplier: Double): Boolean { override fun canAfford(player: Player, multiplier: Double): Boolean {
return (player.account?.getBalance(currency)?.onHand ?: 0f) >= getValue(player, multiplier) return (player.account?.getBalance(currency)?.onHand ?: 0.0) >= getValue(player, multiplier)
} }
override fun pay(player: Player, multiplier: Double) { override fun pay(player: Player, multiplier: Double) {
player.account?.getBalance(currency)?.removeHand(getValue(player, multiplier).toFloat()) player.account?.getBalance(currency)?.removeHand(getValue(player, multiplier))
} }
override fun giveTo(player: Player, multiplier: Double) { override fun giveTo(player: Player, multiplier: Double) {
player.account?.getBalance(currency)?.addHand(getValue(player, multiplier).toFloat()) player.account?.getBalance(currency)?.addHand(getValue(player, multiplier))
} }
override fun getValue(player: Player, multiplier: Double): Double { override fun getValue(player: Player, multiplier: Double): Double {

View File

@@ -10,11 +10,13 @@ class DelegatedExpressionHandler(
plugin: EcoPlugin, plugin: EcoPlugin,
private val handler: ExpressionHandler private val handler: ExpressionHandler
) : ExpressionHandler { ) : ExpressionHandler {
private val evaluationCache: Cache<Int, Double> = Caffeine.newBuilder() private val evaluationCache: Cache<Int, Double?> = Caffeine.newBuilder()
.expireAfterWrite(plugin.configYml.getInt("math-cache-ttl").toLong(), TimeUnit.MILLISECONDS) .expireAfterWrite(plugin.configYml.getInt("math-cache-ttl").toLong(), TimeUnit.MILLISECONDS)
.build() .build()
override fun evaluate(expression: String, context: PlaceholderContext): Double { override fun evaluate(expression: String, context: PlaceholderContext): Double? {
expression.fastToDoubleOrNull()?.let { return it }
// Peak performance (totally not having fun with bitwise operators) // Peak performance (totally not having fun with bitwise operators)
val hash = (((expression.hashCode() shl 5) - expression.hashCode()) xor val hash = (((expression.hashCode() shl 5) - expression.hashCode()) xor
(context.player?.uniqueId?.hashCode() ?: 0) (context.player?.uniqueId?.hashCode() ?: 0)
@@ -22,7 +24,7 @@ class DelegatedExpressionHandler(
return evaluationCache.get(hash) { return evaluationCache.get(hash) {
handler.evaluate(expression, context) handler.evaluate(expression, context)
.let { if (!it.isFinite()) 0.0 else it } // Fixes NaN bug. .let { if (it?.isFinite() != true) null else it } // Fixes NaN bug.
} }
} }
} }

View File

@@ -14,8 +14,6 @@ import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.pow import kotlin.math.pow
private val goToZero = Crunch.compileExpression("0")
private val min = Function("min", 2) { private val min = Function("min", 2) {
min(it[0], it[1]) min(it[0], it[1])
} }
@@ -25,10 +23,13 @@ private val max = Function("max", 2) {
} }
interface ExpressionHandler { interface ExpressionHandler {
fun evaluate(expression: String, context: PlaceholderContext): Double fun evaluate(expression: String, context: PlaceholderContext): Double?
} }
private fun String.fastToDoubleOrNull(): Double? {
if (isEmpty()) { fun String.fastToDoubleOrNull(): Double? {
val len = length
if (len == 0) {
return null return null
} }
@@ -40,13 +41,13 @@ private fun String.fastToDoubleOrNull(): Double? {
var decimalPart = 0.0 var decimalPart = 0.0
var decimalIdx = -1 var decimalIdx = -1
while (idx < length) { while (idx < len) {
when (val char = this[idx]) { when (val char = this[idx]) {
'.' -> { '.' -> {
if (decimalIdx != -1) return null if (decimalIdx != -1) return null
decimalIdx = idx decimalIdx = idx
} }
in '0'..'9' -> { in '0'..'9' -> {
val number = (char.code - '0'.code).toDouble() val number = (char.code - '0'.code).toDouble()
if (decimalIdx != -1) { if (decimalIdx != -1) {
@@ -55,13 +56,14 @@ private fun String.fastToDoubleOrNull(): Double? {
integerPart = integerPart * 10 + number integerPart = integerPart * 10 + number
} }
} }
else -> return null else -> return null
} }
idx++ idx++
} }
decimalPart /= 10.0.pow((length - decimalIdx - 1).toDouble()) decimalPart /= 10.0.pow((len - decimalIdx - 1).toDouble())
return if (isNegative) -(integerPart + decimalPart) else integerPart + decimalPart return if (isNegative) -(integerPart + decimalPart) else integerPart + decimalPart
} }
@@ -70,7 +72,7 @@ private fun String.fastToDoubleOrNull(): Double? {
class ImmediatePlaceholderTranslationExpressionHandler( class ImmediatePlaceholderTranslationExpressionHandler(
private val placeholderParser: PlaceholderParser private val placeholderParser: PlaceholderParser
) : ExpressionHandler { ) : ExpressionHandler {
private val cache: Cache<String, CompiledExpression> = Caffeine.newBuilder() private val cache: Cache<String, CompiledExpression?> = Caffeine.newBuilder()
.expireAfterAccess(500, TimeUnit.MILLISECONDS) .expireAfterAccess(500, TimeUnit.MILLISECONDS)
.build() .build()
@@ -78,38 +80,36 @@ class ImmediatePlaceholderTranslationExpressionHandler(
addFunctions(min, max) addFunctions(min, max)
} }
override fun evaluate(expression: String, context: PlaceholderContext): Double { override fun evaluate(expression: String, context: PlaceholderContext): Double? {
val translatedExpression = placeholderParser.translatePlacholders(expression, context) val translatedExpression = placeholderParser.translatePlacholders(expression, context)
val compiled = cache.get(translatedExpression) { val compiled = cache.get(translatedExpression) {
runCatching { Crunch.compileExpression(translatedExpression, env) } runCatching { Crunch.compileExpression(translatedExpression, env) }.getOrNull()
.getOrDefault(goToZero)
} }
return runCatching { compiled.evaluate() }.getOrDefault(0.0) return runCatching { compiled?.evaluate() }.getOrNull()
} }
} }
class LazyPlaceholderTranslationExpressionHandler( class LazyPlaceholderTranslationExpressionHandler(
private val placeholderParser: PlaceholderParser private val placeholderParser: PlaceholderParser
) : ExpressionHandler { ) : ExpressionHandler {
private val cache: Cache<String, CompiledExpression> = Caffeine.newBuilder() private val cache = mutableMapOf<String, CompiledExpression?>()
.build()
override fun evaluate(expression: String, context: PlaceholderContext): Double { override fun evaluate(expression: String, context: PlaceholderContext): Double? {
val placeholders = PlaceholderManager.findPlaceholdersIn(expression) val placeholders = PlaceholderManager.findPlaceholdersIn(expression)
val placeholderValues = placeholderParser.parseIndividualPlaceholders(placeholders, context) val placeholderValues = placeholderParser.parseIndividualPlaceholders(placeholders, context)
.map { it.fastToDoubleOrNull() ?: 0.0 } .map { it.fastToDoubleOrNull() ?: 0.0 }
.toDoubleArray() .toDoubleArray()
val compiled = cache.get(expression) { val compiled = cache.getOrPut(expression) {
val env = EvaluationEnvironment() val env = EvaluationEnvironment()
env.setVariableNames(*placeholders.toTypedArray()) env.setVariableNames(*placeholders.toTypedArray())
env.addFunctions(min, max) env.addFunctions(min, max)
runCatching { Crunch.compileExpression(expression, env) }.getOrDefault(goToZero) runCatching { Crunch.compileExpression(expression, env) }.getOrNull()
} }
return runCatching { compiled.evaluate(*placeholderValues) }.getOrDefault(0.0) return runCatching { compiled?.evaluate(*placeholderValues) }.getOrNull()
} }
} }

View File

@@ -1,15 +1,17 @@
package com.willfp.eco.internal.spigot.recipes package com.willfp.eco.internal.spigot.recipes
import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.recipe.Recipes import com.willfp.eco.core.recipe.Recipes
import org.bukkit.Keyed import org.bukkit.Keyed
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.inventory.CraftItemEvent import org.bukkit.event.inventory.CraftItemEvent
import org.bukkit.event.inventory.PrepareItemCraftEvent import org.bukkit.event.inventory.PrepareItemCraftEvent
import org.bukkit.event.player.PlayerRecipeDiscoverEvent import org.bukkit.event.player.PlayerRecipeDiscoverEvent
class CraftingRecipeListener : Listener { class CraftingRecipeListener(val plugin: EcoPlugin) : Listener {
@EventHandler @EventHandler
fun preventLearningDisplayedRecipes(event: PlayerRecipeDiscoverEvent) { fun preventLearningDisplayedRecipes(event: PlayerRecipeDiscoverEvent) {
if (!EcoPlugin.getPluginNames().contains(event.recipe.namespace)) { if (!EcoPlugin.getPluginNames().contains(event.recipe.namespace)) {
@@ -22,6 +24,16 @@ class CraftingRecipeListener : Listener {
@EventHandler @EventHandler
fun processListeners(event: PrepareItemCraftEvent) { fun processListeners(event: PrepareItemCraftEvent) {
handlePrepare(event)
if (plugin.configYml.getBool("enforce-preparing-recipes")) {
plugin.scheduler.runLater(1) {
handlePrepare(event)
}
}
}
private fun handlePrepare(event: PrepareItemCraftEvent) {
var recipe = event.recipe as? Keyed var recipe = event.recipe as? Keyed
if (recipe == null) { if (recipe == null) {

View File

@@ -97,3 +97,13 @@ math-cache-ttl: 200
# counts. This is completely anonymous and no personal information is logged. This data # counts. This is completely anonymous and no personal information is logged. This data
# is primarily used for optimisation and server insights. # is primarily used for optimisation and server insights.
playerflow: true playerflow: true
# If the packet display system should activate on items that have no meta. This is disabled
# by default for performance reasons, but if you want to use the packet display system on
# items that have no meta, then you can enable this option.
display-without-meta: false
# If eco should enforce its recipes against other plugins. Turn this on if use custom items from
# non eco-based plugins in recipes and are encountering issues with invisible items or similar.
# This may cause issues with other plugins, so only enable this if you're having issues.
enforce-preparing-recipes: false

View File

@@ -1,3 +1,3 @@
version = 6.62.1 version = 6.65.4
plugin-name = eco plugin-name = eco
kotlin.code.style = official kotlin.code.style = official

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

6
gradlew vendored
View File

@@ -205,6 +205,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \ org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

14
gradlew.bat vendored
View File

@@ -14,7 +14,7 @@
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

Binary file not shown.

View File

@@ -18,6 +18,7 @@ include(":eco-core:core-nms:v1_18_R2")
include(":eco-core:core-nms:v1_19_R1") include(":eco-core:core-nms:v1_19_R1")
include(":eco-core:core-nms:v1_19_R2") include(":eco-core:core-nms:v1_19_R2")
include(":eco-core:core-nms:v1_19_R3") include(":eco-core:core-nms:v1_19_R3")
include(":eco-core:core-nms:v1_20_R1")
include(":eco-core:core-proxy") include(":eco-core:core-proxy")
include(":eco-core:core-plugin") include(":eco-core:core-plugin")
include(":eco-core:core-backend") include(":eco-core:core-backend")