From 18190c8b389e6181fd7a784f9c16a607aef9b9ef Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Feb 2026 20:29:17 +0800 Subject: [PATCH 1/8] don't create a new Function every time Configurable#getConfig is called --- .../configuration/PlaceholderAPIConfig.java | 2 +- .../expansion/Configurable.java | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/at/helpch/placeholderapi/configuration/PlaceholderAPIConfig.java b/src/main/java/at/helpch/placeholderapi/configuration/PlaceholderAPIConfig.java index 3fcf999..21f98c8 100644 --- a/src/main/java/at/helpch/placeholderapi/configuration/PlaceholderAPIConfig.java +++ b/src/main/java/at/helpch/placeholderapi/configuration/PlaceholderAPIConfig.java @@ -98,7 +98,7 @@ public final class PlaceholderAPIConfig { } @NotNull - public Map expansions() { + public ConcurrentHashMap expansions() { return expansions; } diff --git a/src/main/java/at/helpch/placeholderapi/expansion/Configurable.java b/src/main/java/at/helpch/placeholderapi/expansion/Configurable.java index df7dda1..20f358d 100644 --- a/src/main/java/at/helpch/placeholderapi/expansion/Configurable.java +++ b/src/main/java/at/helpch/placeholderapi/expansion/Configurable.java @@ -24,6 +24,7 @@ import at.helpch.placeholderapi.PlaceholderAPIPlugin; import org.jetbrains.annotations.NotNull; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Implementing this interface allows {@link at.helpch.placeholderapi.expansion.PlaceholderExpansion PlaceholderExpansions} @@ -43,20 +44,31 @@ import java.util.Map; * @author Ryan McCarthy */ public interface Configurable { - @NotNull Class provideConfigType(); @NotNull T provideDefault(); + @SuppressWarnings("unchecked") @NotNull default T getConfig() { - if (this instanceof PlaceholderExpansion exp) { - return (T) PlaceholderAPIPlugin.instance().configManager().config().expansions().computeIfAbsent(exp.getIdentifier(), s -> provideDefault()); + if (!(this instanceof PlaceholderExpansion exp)) { + return provideDefault(); } - return provideDefault(); + final ConcurrentHashMap expansionConfigs = PlaceholderAPIPlugin.instance().configManager().config().expansions(); + final String key = exp.getIdentifier(); + + final Object existing = expansionConfigs.get(key); + if (existing != null) { + return (T) existing; + } + + final T def = provideDefault(); + final Object conf = expansionConfigs.putIfAbsent(key, def); + + return (T) (conf != null ? conf : def); } // /** From d338e49b2713093f3fbe08c8874def3a6aef0483 Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Feb 2026 20:30:58 +0800 Subject: [PATCH 2/8] 1.0.8 dev --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 55e1262..bc286c8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } group = "at.helpch" -version = "1.0.7" +version = "1.0.8-DEV-${System.getProperty("BUILD_NUMBER")}" description = "An awesome placeholder provider!" From ecfbd35784a336d73e49a0f18c412423f88cbb6f Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Mar 2026 18:21:08 +0800 Subject: [PATCH 3/8] set server version manually --- src/main/resources/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/manifest.json b/src/main/resources/manifest.json index 1a03dda..c37510c 100644 --- a/src/main/resources/manifest.json +++ b/src/main/resources/manifest.json @@ -5,7 +5,7 @@ "Description": "An awesome placeholder provider", "Authors": [{"Name": "HelpChat"}], "Website": "https://placeholderapi.com", - "ServerVersion": "*", + "ServerVersion": "2026.03.26-89796e57b", "Dependencies": {}, "OptionalDependencies": {}, "DisabledByDefault": false, From 79deb5f7d2f71293b938d09f4930d8c256a910a5 Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Mar 2026 18:23:15 +0800 Subject: [PATCH 4/8] pull new chars replacer from main papi --- .../replacer/CharsReplacer.java | 160 ++++++++++-------- 1 file changed, 87 insertions(+), 73 deletions(-) diff --git a/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java b/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java index 9c443e5..7e67215 100644 --- a/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java +++ b/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java @@ -24,7 +24,6 @@ import java.util.Locale; import java.util.function.Function; import at.helpch.placeholderapi.expansion.PlaceholderExpansion; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.PlayerRef; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,98 +37,113 @@ public final class CharsReplacer implements Replacer { this.closure = closure; } - + /** + * Translates placeholders within the provided text using a high-performance + * character-scanning approach. + * *

The method identifies placeholders delimited by the defined {@link Closure} + * (e.g., %identifier_params% or {identifier_params}). If a placeholder is + * successfully identified, the provided lookup function is used to fetch the + * corresponding {@link PlaceholderExpansion}.

+ * + * @param text The raw text containing potential placeholders to be replaced. + * @param player The {@link OfflinePlayer} to contextually parse the placeholders against. + * May be {@code null} if no player context is available. + * @param lookup A function that maps a lowercase identifier string to a registered + * {@link PlaceholderExpansion}. + * @return A string with all valid placeholders replaced by their respective values. + * Returns the original text if no placeholders are found. + */ @NotNull @Override public String apply(@NotNull final String text, @Nullable final PlayerRef player, @NotNull final Function lookup) { - final char[] chars = text.toCharArray(); - final StringBuilder builder = new StringBuilder(text.length()); + final char head = closure.head; + int startPlaceholder = text.indexOf(head); - final StringBuilder identifier = new StringBuilder(); - final StringBuilder parameters = new StringBuilder(); + if (startPlaceholder == -1) { + return text; + } - for (int i = 0; i < chars.length; i++) { - final char l = chars[i]; + final int length = text.length(); + final StringBuilder builder = new StringBuilder(length + (length >> 3)); + int cursor = 0; - if (l != closure.head || i + 1 >= chars.length) { - builder.append(l); + final char tail = closure.tail; + + loop: do { + // Append plain text preceding the placeholder + if (startPlaceholder > cursor) { + builder.append(text, cursor, startPlaceholder); + } + + final int endPlaceholder = text.indexOf(tail, startPlaceholder + 1); + + if (endPlaceholder == -1) { + builder.append(text, startPlaceholder, length); + return builder.toString(); + } + + int underscoreIndex = -1; + + for (int i = startPlaceholder + 1; i < endPlaceholder; i++) { + final char current = text.charAt(i); + + if (current == ' ' && underscoreIndex == -1) { + // Invalid placeholder (contains space before _). + // Treat the opening symbol as literal text and search for the next one. + builder.append(head); + cursor = startPlaceholder + 1; + startPlaceholder = text.indexOf(head, cursor); + + // Safety check: If no more placeholders exist, break to finalize + if (startPlaceholder == -1) { + break loop; + } + continue loop; + } + + if (current == '_' && underscoreIndex == -1) { + underscoreIndex = i; + } + } + + if (underscoreIndex == -1) { + builder.append(text, startPlaceholder, endPlaceholder + 1); + cursor = endPlaceholder + 1; + startPlaceholder = text.indexOf(head, cursor); continue; } - boolean identified = false; - boolean invalid = true; - boolean hadSpace = false; + String identifier = text.substring(startPlaceholder + 1, underscoreIndex); + String parameters = ""; - while (++i < chars.length) { - final char p = chars[i]; - - if (p == ' ' && !identified) { - hadSpace = true; - break; - } - if (p == closure.tail) { - invalid = false; - break; - } - - if (p == '_' && !identified) { - identified = true; - continue; - } - - if (identified) { - parameters.append(p); - } else { - identifier.append(p); - } + if (underscoreIndex + 1 < endPlaceholder) { + parameters = text.substring(underscoreIndex + 1, endPlaceholder); } - final String identifierString = identifier.toString(); - final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT); - final String parametersString = parameters.toString(); + final PlaceholderExpansion expansion = lookup.apply(identifier.toLowerCase(Locale.ROOT)); + String replacement = null; - identifier.setLength(0); - parameters.setLength(0); - - if (invalid) { - builder.append(closure.head).append(identifierString); - - if (identified) { - builder.append('_').append(parametersString); - } - - if (hadSpace) { - builder.append(' '); - } - continue; + if (expansion != null) { + replacement = expansion.onPlaceholderRequest(player, parameters); } - final PlaceholderExpansion placeholder = lookup.apply(lowercaseIdentifierString); - if (placeholder == null) { - builder.append(closure.head).append(identifierString); - - if (identified) { - builder.append('_'); - } - - builder.append(parametersString).append(closure.tail); - continue; + if (replacement != null) { + builder.append(replacement); + } else { + // Fallback: Restore original placeholder format + builder.append(head).append(identifier); + builder.append('_').append(parameters); + builder.append(tail); } - final String replacement = placeholder.onPlaceholderRequest(player, parametersString); - if (replacement == null) { - builder.append(closure.head).append(identifierString); + cursor = endPlaceholder + 1; + startPlaceholder = text.indexOf(head, cursor); - if (identified) { - builder.append('_'); - } + } while (startPlaceholder != -1); - builder.append(parametersString).append(closure.tail); - continue; - } - - builder.append(replacement); + if (cursor < length) { + builder.append(text, cursor, length); } return builder.toString(); From 683815679fee282428c7427357c78efcf3c29af0 Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Mar 2026 18:29:25 +0800 Subject: [PATCH 5/8] don't fail when javadocs are missing smh --- build.gradle.kts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index bc286c8..461a6b3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,10 @@ tasks { eachFile { expand("version" to project.version) } } + withType.configureEach { + isFailOnError = false + } + withType { archiveClassifier.set("hytale") @@ -75,4 +79,4 @@ tasks { } publish.get().setDependsOn(listOf(build.get())) -} \ No newline at end of file +} From 453f93da75d512aadd19b1df8119dde14e0cbfba Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Mar 2026 18:30:08 +0800 Subject: [PATCH 6/8] remove .configureEach from javadoc task --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 461a6b3..1034042 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,7 @@ tasks { eachFile { expand("version" to project.version) } } - withType.configureEach { + withType { isFailOnError = false } From c14c1145e47102266eea05eefb09ce61c95ea34e Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Mar 2026 18:30:41 +0800 Subject: [PATCH 7/8] update bukkit javadoc -> hytale --- .../java/at/helpch/placeholderapi/replacer/CharsReplacer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java b/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java index 7e67215..9a79ac9 100644 --- a/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java +++ b/src/main/java/at/helpch/placeholderapi/replacer/CharsReplacer.java @@ -46,7 +46,7 @@ public final class CharsReplacer implements Replacer { * corresponding {@link PlaceholderExpansion}.

* * @param text The raw text containing potential placeholders to be replaced. - * @param player The {@link OfflinePlayer} to contextually parse the placeholders against. + * @param player The {@link PlayerRef} to contextually parse the placeholders against. * May be {@code null} if no player context is available. * @param lookup A function that maps a lowercase identifier string to a registered * {@link PlaceholderExpansion}. From 7313622a8ab583d602aaea9cb69e8c5b96c59f89 Mon Sep 17 00:00:00 2001 From: PiggyPiglet Date: Fri, 27 Mar 2026 18:44:51 +0800 Subject: [PATCH 8/8] 1.0.8 release --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1034042..6378995 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } group = "at.helpch" -version = "1.0.8-DEV-${System.getProperty("BUILD_NUMBER")}" +version = "1.0.8" description = "An awesome placeholder provider!"