mirror of
https://github.com/PlaceholderAPI/PlaceholderAPI
synced 2026-02-28 06:35:11 +01:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
feb4eff237 | ||
|
|
7286d8849b | ||
|
|
1807b92505 | ||
|
|
3c82c6cdd7 | ||
|
|
17ad663257 | ||
|
|
984ec7f7b9 | ||
|
|
c8f73a5940 | ||
|
|
81675c1fd6 | ||
|
|
69cef6f72b | ||
|
|
ca2bd9f19a | ||
|
|
b331145c15 | ||
|
|
b085e49062 | ||
|
|
43f6a517af | ||
|
|
59769c2d93 | ||
|
|
354aebe8df | ||
|
|
566beb48c5 | ||
|
|
64b3d6fa68 | ||
|
|
975b1ac3ca | ||
|
|
7b8550c3f1 | ||
|
|
f267887b5e | ||
|
|
e187eb6402 | ||
|
|
0d1a356e0f | ||
|
|
407b9f5e54 | ||
|
|
469997e114 | ||
|
|
8185b7bfe9 | ||
|
|
fe15e4ed7a | ||
|
|
0f35362a0c | ||
|
|
9a4fa18304 | ||
|
|
cb5d6c0895 | ||
|
|
3684e32ac0 |
12
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
12
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -18,13 +18,13 @@ body:
|
|||||||
label: Confirmation
|
label: Confirmation
|
||||||
description: Please make sure to have followed the following checks.
|
description: Please make sure to have followed the following checks.
|
||||||
options:
|
options:
|
||||||
- label: My issue isn't already found on the Issue tracker.
|
- label: "My issue isn't already found on the Issue tracker."
|
||||||
required: true
|
required: true
|
||||||
- label: My issue is about **PlaceholderAPI** and not any expansion or external plugin
|
- label: "My issue is about **PlaceholderAPI** and not any expansion or external plugin."
|
||||||
required: true
|
required: true
|
||||||
- label: The issue isn't already fixed in a Spigot Release or Development Build.
|
- label: "The issue isn't already fixed in a Spigot Release or Development Build."
|
||||||
required: true
|
required: true
|
||||||
- label: The [Common Issues](https://github.com/PlaceholderAPI/PlaceholderAPI/wiki/Common-Issues) page doesn't mention this issue.
|
- label: "The [Common Issues](https://wiki.placeholderapi.com/common-issues/) page doesn't mention this issue."
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
@@ -85,6 +85,8 @@ body:
|
|||||||
description: |-
|
description: |-
|
||||||
Get the latest content of your `latest.log` file an upload it to https://paste.helpch.at
|
Get the latest content of your `latest.log` file an upload it to https://paste.helpch.at
|
||||||
Take the generated URL and paste it into this field.
|
Take the generated URL and paste it into this field.
|
||||||
|
|
||||||
|
**Always provide the full `latest.log` and not just parts of it or just the error (if any)!**
|
||||||
placeholder: "https://paste.helpch.at/latest.log"
|
placeholder: "https://paste.helpch.at/latest.log"
|
||||||
- type: input
|
- type: input
|
||||||
id: "error"
|
id: "error"
|
||||||
@@ -99,5 +101,5 @@ body:
|
|||||||
description: |-
|
description: |-
|
||||||
Add any extra info you think is nessesary for this Bug report.
|
Add any extra info you think is nessesary for this Bug report.
|
||||||
- If you selected `API Bug` will you need to include code-examples here to reproduce the issue.
|
- If you selected `API Bug` will you need to include code-examples here to reproduce the issue.
|
||||||
- If you selected `Plugin/Server Incompatability` should you include extra Server info such as a Timings or Spark-Report or info about the plugin in question.
|
- If you selected `Plugin/Server Incompatability` should you include extra Server info such as Spark-Report or info about the plugin in question.
|
||||||
placeholder: "Put any extra info you like into this field..."
|
placeholder: "Put any extra info you like into this field..."
|
||||||
|
|||||||
55
.github/workflows/pr_wiki_validation.yml
vendored
Normal file
55
.github/workflows/pr_wiki_validation.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#
|
||||||
|
# Validates Pull requests targeting the wiki branch
|
||||||
|
# to ensure that the site can be build successfully
|
||||||
|
# without broken links, navigation, etc.
|
||||||
|
#
|
||||||
|
name: "Validate Wiki Build"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
paths-ignore:
|
||||||
|
- "README.md"
|
||||||
|
branches:
|
||||||
|
- wiki
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
buildWiki:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: "Checkout Repository"
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: "${{ github.event.pull_request.head.sha }}"
|
||||||
|
- name: "Setup Python 3.x"
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
- name: "Install dependencies"
|
||||||
|
run: "python -m pip install mkdocs-material"
|
||||||
|
- name: "Build Site"
|
||||||
|
run: "mkdocs build --strict"
|
||||||
|
- name: "Create Comment"
|
||||||
|
uses: peter-evans/create-or-update-comment@v5
|
||||||
|
if: ${{ failure() }}
|
||||||
|
with:
|
||||||
|
body: |-
|
||||||
|
## Wiki Build failure
|
||||||
|
|
||||||
|
Something went wrong while creating a test-build of the Wiki for this Pull request.
|
||||||
|
Please check the [Workflow Logs](${{ env.RUN_URL }}) for any errors.
|
||||||
|
issue-number: "${{ github.event.pull_request.number }}"
|
||||||
|
token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
edit-mode: replace
|
||||||
@@ -4,11 +4,12 @@ plugins {
|
|||||||
`java-library`
|
`java-library`
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
// id("com.github.hierynomus.license") version "0.16.1"
|
// id("com.github.hierynomus.license") version "0.16.1"
|
||||||
id("io.github.goooler.shadow") version "8.1.7"
|
id("com.gradleup.shadow") version "9.3.1"
|
||||||
|
id("me.champeau.jmh") version "0.7.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "me.clip"
|
group = "me.clip"
|
||||||
version = "2.12.2"
|
version = "2.12.3-DEV-${System.getProperty("BUILD_NUMBER")}"
|
||||||
|
|
||||||
description = "An awesome placeholder provider!"
|
description = "An awesome placeholder provider!"
|
||||||
|
|
||||||
@@ -41,12 +42,13 @@ dependencies {
|
|||||||
compileOnly("dev.folia:folia-api:1.21.11-R0.1-SNAPSHOT")
|
compileOnly("dev.folia:folia-api:1.21.11-R0.1-SNAPSHOT")
|
||||||
compileOnlyApi("org.jetbrains:annotations:23.0.0")
|
compileOnlyApi("org.jetbrains:annotations:23.0.0")
|
||||||
|
|
||||||
testImplementation("org.openjdk.jmh:jmh-core:1.32")
|
jmh("org.openjdk.jmh:jmh-core:1.37")
|
||||||
testImplementation("org.openjdk.jmh:jmh-generator-annprocess:1.32")
|
jmh("org.openjdk.jmh:jmh-generator-annprocess:1.37")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.8.2")
|
jmhAnnotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.37")
|
||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter:6.0.2")
|
||||||
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
94
src/jmh/java/me/clip/placeholderapi/Values.java
Normal file
94
src/jmh/java/me/clip/placeholderapi/Values.java
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of PlaceholderAPI
|
||||||
|
*
|
||||||
|
* PlaceholderAPI
|
||||||
|
* Copyright (c) 2015 - 2026 PlaceholderAPI Team
|
||||||
|
*
|
||||||
|
* PlaceholderAPI free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PlaceholderAPI is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package me.clip.placeholderapi;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||||
|
import me.clip.placeholderapi.replacer.CharsReplacer;
|
||||||
|
import me.clip.placeholderapi.replacer.OldCharsReplacer;
|
||||||
|
import me.clip.placeholderapi.replacer.Replacer;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface Values {
|
||||||
|
|
||||||
|
String SMALL_TEXT = "My name is %player_name%";
|
||||||
|
String LARGE_TEXT = "My name is %player_name% and my location is (%player_x%, %player_y%, %player_z%), this placeholder is invalid %server_name%";
|
||||||
|
|
||||||
|
ImmutableMap<String, PlaceholderExpansion> PLACEHOLDERS = ImmutableMap.<String, PlaceholderExpansion>builder()
|
||||||
|
.put("player", new MockPlayerPlaceholderExpansion())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
Replacer CHARS_REPLACER = new CharsReplacer(Replacer.Closure.PERCENT);
|
||||||
|
Replacer OLD_CHARS_REPLACER = new OldCharsReplacer(Replacer.Closure.PERCENT);
|
||||||
|
|
||||||
|
final class MockPlayerPlaceholderExpansion extends PlaceholderExpansion {
|
||||||
|
|
||||||
|
public static final String PLAYER_X = "10";
|
||||||
|
public static final String PLAYER_Y = "20";
|
||||||
|
public static final String PLAYER_Z = "30";
|
||||||
|
public static final String PLAYER_NAME = "Sxtanna";
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
return "player";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getAuthor() {
|
||||||
|
return "Sxtanna";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getVersion() {
|
||||||
|
return "1.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String onRequest(@Nullable final OfflinePlayer player, @NotNull final String params) {
|
||||||
|
final String[] parts = params.split("_");
|
||||||
|
if (parts.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parts[0]) {
|
||||||
|
case "name":
|
||||||
|
return PLAYER_NAME;
|
||||||
|
case "x":
|
||||||
|
return PLAYER_X;
|
||||||
|
case "y":
|
||||||
|
return PLAYER_Y;
|
||||||
|
case "z":
|
||||||
|
return PLAYER_Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package me.clip.placeholderapi.replacer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PlaceholderAPI
|
||||||
|
*
|
||||||
|
* PlaceholderAPI
|
||||||
|
* Copyright (c) 2015 - 2026 PlaceholderAPI Team
|
||||||
|
*
|
||||||
|
* PlaceholderAPI free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PlaceholderAPI is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public final class OldCharsReplacer implements Replacer {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Closure closure;
|
||||||
|
|
||||||
|
public OldCharsReplacer(@NotNull final Closure closure) {
|
||||||
|
this.closure = closure;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String apply(@NotNull final String text, @Nullable final OfflinePlayer player,
|
||||||
|
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
|
||||||
|
final char[] chars = text.toCharArray();
|
||||||
|
final StringBuilder builder = new StringBuilder(text.length());
|
||||||
|
|
||||||
|
final StringBuilder identifier = new StringBuilder();
|
||||||
|
final StringBuilder parameters = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < chars.length; i++) {
|
||||||
|
final char l = chars[i];
|
||||||
|
|
||||||
|
if (l != closure.head || i + 1 >= chars.length) {
|
||||||
|
builder.append(l);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean identified = false;
|
||||||
|
boolean invalid = true;
|
||||||
|
boolean hadSpace = false;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String identifierString = identifier.toString();
|
||||||
|
final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT);
|
||||||
|
final String parametersString = parameters.toString();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String replacement = placeholder.onRequest(player, parametersString);
|
||||||
|
if (replacement == null) {
|
||||||
|
builder.append(closure.head).append(identifierString);
|
||||||
|
|
||||||
|
if (identified) {
|
||||||
|
builder.append('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(parametersString).append(closure.tail);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of PlaceholderAPI
|
||||||
|
*
|
||||||
|
* PlaceholderAPI
|
||||||
|
* Copyright (c) 2015 - 2026 PlaceholderAPI Team
|
||||||
|
*
|
||||||
|
* PlaceholderAPI free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* PlaceholderAPI is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package me.clip.placeholderapi.replacer;
|
||||||
|
|
||||||
|
import me.clip.placeholderapi.Values;
|
||||||
|
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.openjdk.jmh.annotations.*;
|
||||||
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||||
|
@BenchmarkMode({Mode.AverageTime, Mode.Throughput})
|
||||||
|
@Fork(value = 3, warmups = 1)
|
||||||
|
@Warmup(iterations = 5, time = 1)
|
||||||
|
@Measurement(iterations = 10, time = 1)
|
||||||
|
public class ReplacerBenchmarks {
|
||||||
|
|
||||||
|
private Function<String, @Nullable PlaceholderExpansion> expansionFunction;
|
||||||
|
|
||||||
|
@Setup
|
||||||
|
public void setup() {
|
||||||
|
this.expansionFunction = Values.PLACEHOLDERS::get;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void measureCharsReplacerSmallText(final Blackhole blackhole) {
|
||||||
|
blackhole.consume(Values.CHARS_REPLACER.apply(Values.SMALL_TEXT, null, expansionFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void measureCharsReplacerLargeText(final Blackhole blackhole) {
|
||||||
|
blackhole.consume(Values.CHARS_REPLACER.apply(Values.LARGE_TEXT, null, expansionFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void measureCharsReplacerSmallTextOld(final Blackhole blackhole) {
|
||||||
|
blackhole.consume(Values.OLD_CHARS_REPLACER.apply(Values.SMALL_TEXT, null, expansionFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void measureCharsReplacerLargeTextOld(final Blackhole blackhole) {
|
||||||
|
blackhole.consume(Values.OLD_CHARS_REPLACER.apply(Values.LARGE_TEXT, null, expansionFunction));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,12 +20,7 @@
|
|||||||
|
|
||||||
package me.clip.placeholderapi;
|
package me.clip.placeholderapi;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import java.util.*;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -85,7 +80,11 @@ public final class PlaceholderAPI {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public static List<String> setPlaceholders(final OfflinePlayer player,
|
public static List<String> setPlaceholders(final OfflinePlayer player,
|
||||||
@NotNull final List<String> text) {
|
@NotNull final List<String> text) {
|
||||||
return text.stream().map(line -> setPlaceholders(player, line)).collect(Collectors.toList());
|
final List<String> result = new ArrayList<>(text.size());
|
||||||
|
for (final String line : text) {
|
||||||
|
result.add(setPlaceholders(player, line));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,8 +139,11 @@ public final class PlaceholderAPI {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public static List<@NotNull String> setBracketPlaceholders(final OfflinePlayer player,
|
public static List<@NotNull String> setBracketPlaceholders(final OfflinePlayer player,
|
||||||
@NotNull final List<@NotNull String> text) {
|
@NotNull final List<@NotNull String> text) {
|
||||||
return text.stream().map(line -> setBracketPlaceholders(player, line))
|
final List<String> result = new ArrayList<>(text.size());
|
||||||
.collect(Collectors.toList());
|
for (final String line : text) {
|
||||||
|
result.add(setBracketPlaceholders(player, line));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,14 +181,14 @@ public final class PlaceholderAPI {
|
|||||||
* @param text Text to parse the placeholders in
|
* @param text Text to parse the placeholders in
|
||||||
* @return The text containing the parsed relational placeholders
|
* @return The text containing the parsed relational placeholders
|
||||||
*/
|
*/
|
||||||
public static String setRelationalPlaceholders(Player one, Player two, String text) {
|
public static String setRelationalPlaceholders(final Player one, final Player two, @NotNull String text) {
|
||||||
final Matcher matcher = RELATIONAL_PLACEHOLDER_PATTERN.matcher(text);
|
final Matcher matcher = RELATIONAL_PLACEHOLDER_PATTERN.matcher(text);
|
||||||
|
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
final String format = matcher.group(2);
|
final String format = matcher.group(2);
|
||||||
final int index = format.indexOf("_");
|
final int index = format.indexOf('_');
|
||||||
|
|
||||||
if (index <= 0 || index >= format.length()) {
|
if (index <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,9 +220,12 @@ public final class PlaceholderAPI {
|
|||||||
* @param text text to parse the placeholder values to
|
* @param text text to parse the placeholder values to
|
||||||
* @return The text containing the parsed relational placeholders
|
* @return The text containing the parsed relational placeholders
|
||||||
*/
|
*/
|
||||||
public static List<String> setRelationalPlaceholders(Player one, Player two, List<String> text) {
|
public static List<String> setRelationalPlaceholders(final Player one, final Player two, final @NotNull List<String> text) {
|
||||||
return text.stream().map(line -> setRelationalPlaceholders(one, two, line))
|
final List<String> result = new ArrayList<>(text.size());
|
||||||
.collect(Collectors.toList());
|
for (final String line : text) {
|
||||||
|
result.add(setRelationalPlaceholders(one, two, line));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,8 +246,7 @@ public final class PlaceholderAPI {
|
|||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Set<String> getRegisteredIdentifiers() {
|
public static Set<String> getRegisteredIdentifiers() {
|
||||||
return ImmutableSet
|
return PlaceholderAPIPlugin.getInstance().getLocalExpansionManager().getIdentifiers();
|
||||||
.copyOf(PlaceholderAPIPlugin.getInstance().getLocalExpansionManager().getIdentifiers());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -279,8 +283,15 @@ public final class PlaceholderAPI {
|
|||||||
* @param text String to check
|
* @param text String to check
|
||||||
* @return true if String contains any matches to the normal placeholder pattern, false otherwise
|
* @return true if String contains any matches to the normal placeholder pattern, false otherwise
|
||||||
*/
|
*/
|
||||||
public static boolean containsPlaceholders(String text) {
|
public static boolean containsPlaceholders(final String text) {
|
||||||
return text != null && PLACEHOLDER_PATTERN.matcher(text).find();
|
if (text == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final int firstPercent = text.indexOf('%');
|
||||||
|
if (firstPercent == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return text.indexOf('%', firstPercent + 1) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,8 +301,15 @@ public final class PlaceholderAPI {
|
|||||||
* @param text String to check
|
* @param text String to check
|
||||||
* @return true if String contains any matches to the bracket placeholder pattern, false otherwise
|
* @return true if String contains any matches to the bracket placeholder pattern, false otherwise
|
||||||
*/
|
*/
|
||||||
public static boolean containsBracketPlaceholders(String text) {
|
public static boolean containsBracketPlaceholders(final String text) {
|
||||||
return text != null && BRACKET_PLACEHOLDER_PATTERN.matcher(text).find();
|
if (text == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final int openBracket = text.indexOf('{');
|
||||||
|
if (openBracket == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return text.indexOf('}', openBracket + 1) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Deprecated API ===
|
// === Deprecated API ===
|
||||||
|
|||||||
@@ -26,12 +26,9 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.net.URL;
|
import java.net.*;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -61,6 +58,24 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Unmodifiable;
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
public final class CloudExpansionManager {
|
public final class CloudExpansionManager {
|
||||||
|
@NotNull
|
||||||
|
public static final String USER_AGENT;
|
||||||
|
|
||||||
|
static {
|
||||||
|
String userAgent;
|
||||||
|
|
||||||
|
try (final InputStream in = PlaceholderAPIPlugin.class.getResourceAsStream("/user-agent.txt")) {
|
||||||
|
if (in == null) {
|
||||||
|
userAgent = "PlaceholderAPI-Bukkit-null";
|
||||||
|
} else {
|
||||||
|
userAgent = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)).readLine();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
userAgent = "PlaceholderAPI-Bukkit-null";
|
||||||
|
}
|
||||||
|
|
||||||
|
USER_AGENT = userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static final String API_URL = "https://ecloud.placeholderapi.com/api/v3/?platform=bukkit";
|
private static final String API_URL = "https://ecloud.placeholderapi.com/api/v3/?platform=bukkit";
|
||||||
@@ -185,8 +200,16 @@ public final class CloudExpansionManager {
|
|||||||
// a defence tactic! use ConcurrentHashMap instead of normal HashMap
|
// a defence tactic! use ConcurrentHashMap instead of normal HashMap
|
||||||
Map<String, CloudExpansion> values = new ConcurrentHashMap<>();
|
Map<String, CloudExpansion> values = new ConcurrentHashMap<>();
|
||||||
try {
|
try {
|
||||||
//noinspection UnstableApiUsage
|
final URI uri = new URI(API_URL);
|
||||||
String json = Resources.toString(new URL(API_URL), StandardCharsets.UTF_8);
|
final URLConnection connection = uri.toURL().openConnection();
|
||||||
|
connection.setRequestProperty("User-Agent", USER_AGENT);
|
||||||
|
final String json;
|
||||||
|
|
||||||
|
try (final InputStream in = connection.getInputStream()) {
|
||||||
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||||
|
json = reader.lines().collect(Collectors.joining(System.lineSeparator()));
|
||||||
|
}
|
||||||
|
|
||||||
values.putAll(GSON.fromJson(json, TYPE));
|
values.putAll(GSON.fromJson(json, TYPE));
|
||||||
|
|
||||||
List<String> toRemove = new ArrayList<>();
|
List<String> toRemove = new ArrayList<>();
|
||||||
@@ -260,10 +283,14 @@ public final class CloudExpansionManager {
|
|||||||
"Expansion-" + toIndexName(expansion) + ".jar");
|
"Expansion-" + toIndexName(expansion) + ".jar");
|
||||||
|
|
||||||
final CompletableFuture<File> download = CompletableFuture.supplyAsync(() -> {
|
final CompletableFuture<File> download = CompletableFuture.supplyAsync(() -> {
|
||||||
try (final ReadableByteChannel source = Channels.newChannel(new URL(version.getUrl())
|
try {
|
||||||
.openStream()); final FileOutputStream target = new FileOutputStream(file)) {
|
final URLConnection connection = new URI(version.getUrl()).toURL().openConnection();
|
||||||
target.getChannel().transferFrom(source, 0, Long.MAX_VALUE);
|
connection.setRequestProperty("User-Agent", USER_AGENT);
|
||||||
} catch (final IOException ex) {
|
|
||||||
|
try (final ReadableByteChannel source = Channels.newChannel(connection.getInputStream()); final FileOutputStream target = new FileOutputStream(file)) {
|
||||||
|
target.getChannel().transferFrom(source, 0, Long.MAX_VALUE);
|
||||||
|
}
|
||||||
|
} catch (final IOException | URISyntaxException ex) {
|
||||||
throw new CompletionException(ex);
|
throw new CompletionException(ex);
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public final class LocalExpansionManager implements Listener {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Unmodifiable
|
@Unmodifiable
|
||||||
public Collection<String> getIdentifiers() {
|
public Set<String> getIdentifiers() {
|
||||||
expansionsLock.lock();
|
expansionsLock.lock();
|
||||||
try {
|
try {
|
||||||
return ImmutableSet.copyOf(expansions.keySet());
|
return ImmutableSet.copyOf(expansions.keySet());
|
||||||
@@ -136,12 +136,7 @@ public final class LocalExpansionManager implements Listener {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public PlaceholderExpansion getExpansion(@NotNull final String identifier) {
|
public PlaceholderExpansion getExpansion(@NotNull final String identifier) {
|
||||||
expansionsLock.lock();
|
return expansions.get(identifier.toLowerCase(Locale.ROOT));
|
||||||
try {
|
|
||||||
return expansions.get(identifier.toLowerCase(Locale.ROOT));
|
|
||||||
} finally {
|
|
||||||
expansionsLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ package me.clip.placeholderapi.replacer;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||||
import org.bukkit.ChatColor;
|
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -38,98 +36,113 @@ public final class CharsReplacer implements Replacer {
|
|||||||
this.closure = closure;
|
this.closure = closure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates placeholders within the provided text using a high-performance
|
||||||
|
* character-scanning approach.
|
||||||
|
* * <p>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}.</p>
|
||||||
|
*
|
||||||
|
* @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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String apply(@NotNull final String text, @Nullable final OfflinePlayer player,
|
public String apply(@NotNull final String text, @Nullable final OfflinePlayer player,
|
||||||
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
|
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
|
||||||
final char[] chars = text.toCharArray();
|
final char head = closure.head;
|
||||||
final StringBuilder builder = new StringBuilder(text.length());
|
int startPlaceholder = text.indexOf(head);
|
||||||
|
|
||||||
final StringBuilder identifier = new StringBuilder();
|
if (startPlaceholder == -1) {
|
||||||
final StringBuilder parameters = new StringBuilder();
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < chars.length; i++) {
|
final int length = text.length();
|
||||||
final char l = chars[i];
|
final StringBuilder builder = new StringBuilder(length + (length >> 3));
|
||||||
|
int cursor = 0;
|
||||||
|
|
||||||
if (l != closure.head || i + 1 >= chars.length) {
|
final char tail = closure.tail;
|
||||||
builder.append(l);
|
|
||||||
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean identified = false;
|
String identifier = text.substring(startPlaceholder + 1, underscoreIndex);
|
||||||
boolean invalid = true;
|
String parameters = "";
|
||||||
boolean hadSpace = false;
|
|
||||||
|
|
||||||
while (++i < chars.length) {
|
if (underscoreIndex + 1 < endPlaceholder) {
|
||||||
final char p = chars[i];
|
parameters = text.substring(underscoreIndex + 1, endPlaceholder);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String identifierString = identifier.toString();
|
final PlaceholderExpansion expansion = lookup.apply(identifier.toLowerCase(Locale.ROOT));
|
||||||
final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT);
|
String replacement = null;
|
||||||
final String parametersString = parameters.toString();
|
|
||||||
|
|
||||||
identifier.setLength(0);
|
if (expansion != null) {
|
||||||
parameters.setLength(0);
|
replacement = expansion.onRequest(player, parameters);
|
||||||
|
|
||||||
if (invalid) {
|
|
||||||
builder.append(closure.head).append(identifierString);
|
|
||||||
|
|
||||||
if (identified) {
|
|
||||||
builder.append('_').append(parametersString);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hadSpace) {
|
|
||||||
builder.append(' ');
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final PlaceholderExpansion placeholder = lookup.apply(lowercaseIdentifierString);
|
if (replacement != null) {
|
||||||
if (placeholder == null) {
|
builder.append(replacement);
|
||||||
builder.append(closure.head).append(identifierString);
|
} else {
|
||||||
|
// Fallback: Restore original placeholder format
|
||||||
if (identified) {
|
builder.append(head).append(identifier);
|
||||||
builder.append('_');
|
builder.append('_').append(parameters);
|
||||||
}
|
builder.append(tail);
|
||||||
|
|
||||||
builder.append(parametersString).append(closure.tail);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String replacement = placeholder.onRequest(player, parametersString);
|
cursor = endPlaceholder + 1;
|
||||||
if (replacement == null) {
|
startPlaceholder = text.indexOf(head, cursor);
|
||||||
builder.append(closure.head).append(identifierString);
|
|
||||||
|
|
||||||
if (identified) {
|
} while (startPlaceholder != -1);
|
||||||
builder.append('_');
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append(parametersString).append(closure.tail);
|
if (cursor < length) {
|
||||||
continue;
|
builder.append(text, cursor, length);
|
||||||
}
|
|
||||||
|
|
||||||
builder.append(replacement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
|
|||||||
@@ -4,10 +4,15 @@ import com.google.common.hash.Hashing;
|
|||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||||
|
import me.clip.placeholderapi.expansion.manager.CloudExpansionManager;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -47,7 +52,10 @@ public final class ExpansionSafetyCheck {
|
|||||||
final Set<String> knownMaliciousExpansions;
|
final Set<String> knownMaliciousExpansions;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final String hashes = Resources.toString(new URL("https://check.placeholderapi.com"), StandardCharsets.UTF_8);
|
final URLConnection connection = new URI("https://check.placeholderapi.com").toURL().openConnection();
|
||||||
|
connection.setRequestProperty("User-Agent", CloudExpansionManager.USER_AGENT);
|
||||||
|
final String hashes = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))
|
||||||
|
.lines().collect(Collectors.joining(System.lineSeparator()));
|
||||||
knownMaliciousExpansions = Arrays.stream(hashes.split("\n")).collect(Collectors.toSet());
|
knownMaliciousExpansions = Arrays.stream(hashes.split("\n")).collect(Collectors.toSet());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
main.getLogger().log(Level.SEVERE, "Failed to download anti malware hash check list from https://check.placeholderapi.com", e);
|
main.getLogger().log(Level.SEVERE, "Failed to download anti malware hash check list from https://check.placeholderapi.com", e);
|
||||||
@@ -55,9 +63,18 @@ public final class ExpansionSafetyCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Set<String> maliciousPaths = new HashSet<>();
|
final Set<String> maliciousPaths = new HashSet<>();
|
||||||
|
final File[] files = expansionsFolder.listFiles();
|
||||||
|
|
||||||
for (File file : expansionsFolder.listFiles()) {
|
if (files == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (File file : files) {
|
||||||
try {
|
try {
|
||||||
|
if (!file.isFile()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final String hash = Hashing.sha256().hashBytes(Files.asByteSource(file).read()).toString();
|
final String hash = Hashing.sha256().hashBytes(Files.asByteSource(file).read()).toString();
|
||||||
|
|
||||||
if (knownMaliciousExpansions.contains(hash)) {
|
if (knownMaliciousExpansions.contains(hash)) {
|
||||||
|
|||||||
1
src/main/resources/user-agent.txt
Normal file
1
src/main/resources/user-agent.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PlaceholderAPI-Bukkit-${version}
|
||||||
@@ -25,11 +25,14 @@ import me.clip.placeholderapi.replacer.ExactReplacer;
|
|||||||
import me.clip.placeholderapi.replacer.RelationalExactReplacer;
|
import me.clip.placeholderapi.replacer.RelationalExactReplacer;
|
||||||
import me.clip.placeholderapi.replacer.Replacer;
|
import me.clip.placeholderapi.replacer.Replacer;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.ComponentLike;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static me.clip.placeholderapi.PlaceholderAPI.RELATIONAL_PLACEHOLDER_PATTERN;
|
import static me.clip.placeholderapi.PlaceholderAPI.RELATIONAL_PLACEHOLDER_PATTERN;
|
||||||
@@ -43,122 +46,236 @@ public final class PAPIComponents {
|
|||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
* @param component Component to set the placeholder values in
|
* @param component Component to set the placeholder values in
|
||||||
* @return Component containing all translated placeholders
|
* @return Component containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Component setPlaceholders(final OfflinePlayer player, @NotNull final Component component) {
|
public static Component setPlaceholders(final OfflinePlayer player, @NotNull final Component component) {
|
||||||
if (PlaceholderAPIPlugin.getInstance().getPlaceholderAPIConfig().useAdventureProvidedReplacer()) {
|
return setPlaceholders(player, component, null);
|
||||||
return component.replaceText(config -> config.match(PlaceholderAPI.PLACEHOLDER_PATTERN).replacement((result, builder) ->
|
|
||||||
builder.content(PERCENT_EXACT_REPLACER.apply(result.group(), player, PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()::getExpansion))));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComponentReplacer.replace(component, str -> PlaceholderAPI.setPlaceholders(player, str));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param component Component to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return Component containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Component setPlaceholders(final OfflinePlayer player, @NotNull final Component component, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
|
if (PlaceholderAPIPlugin.getInstance().getPlaceholderAPIConfig().useAdventureProvidedReplacer()) {
|
||||||
|
return component.replaceText(config -> config.match(PlaceholderAPI.PLACEHOLDER_PATTERN).replacement((result, builder) -> {
|
||||||
|
String parsed = PERCENT_EXACT_REPLACER.apply(result.group(), player, PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()::getExpansion);
|
||||||
|
return deserializer == null ? builder.content(parsed) : deserializer.apply(parsed);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ComponentReplacer.replace(component, str -> PlaceholderAPI.setPlaceholders(player, str), deserializer == null ? null : s -> deserializer.apply(s).asComponent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
* @param components List of Components to set the placeholder values in
|
* @param components List of Components to set the placeholder values in
|
||||||
* @return List of Components containing all translated placeholders
|
* @return List of Components containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static List<Component> setPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components) {
|
public static List<Component> setPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components) {
|
||||||
return components.stream().map(component -> setPlaceholders(player, component)).collect(Collectors.toList());
|
return setPlaceholders(player, components, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param components List of Components to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return List of Components containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static List<Component> setPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
|
return components.stream().map(component -> setPlaceholders(player, component, deserializer == null ? null : s -> deserializer.apply(s).asComponent())).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
* @param component Component to set the placeholder values in
|
* @param component Component to set the placeholder values in
|
||||||
* @return Component containing all translated placeholders
|
* @return Component containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Component setPlaceholders(final Player player, @NotNull final Component component) {
|
public static Component setPlaceholders(final Player player, @NotNull final Component component) {
|
||||||
return setPlaceholders((OfflinePlayer) player, component);
|
return setPlaceholders(player, component, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param component Component to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return Component containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Component setPlaceholders(final Player player, @NotNull final Component component, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
|
return setPlaceholders((OfflinePlayer) player, component, deserializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
* @param components List of Components to set the placeholder values in
|
* @param components List of Components to set the placeholder values in
|
||||||
* @return List of components containing all translated placeholders
|
* @return List of components containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static List<Component> setPlaceholders(final Player player, @NotNull final List<Component> components) {
|
public static List<Component> setPlaceholders(final Player player, @NotNull final List<Component> components) {
|
||||||
return setPlaceholders((OfflinePlayer) player, components);
|
return setPlaceholders(player, components, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal %<identifier>_<params>%}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param components List of Components to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return List of components containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static List<Component> setPlaceholders(final Player player, @NotNull final List<Component> components, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
|
return setPlaceholders((OfflinePlayer) player, components, deserializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
* @param component Component to set the placeholder values in
|
* @param component Component to set the placeholder values in
|
||||||
* @return Component containing all translated placeholders
|
* @return Component containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Component setBracketPlaceholders(final OfflinePlayer player, @NotNull final Component component) {
|
public static Component setBracketPlaceholders(final OfflinePlayer player, @NotNull final Component component) {
|
||||||
|
return setBracketPlaceholders(player, component, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param component Component to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return Component containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Component setBracketPlaceholders(final OfflinePlayer player, @NotNull final Component component, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
if (PlaceholderAPIPlugin.getInstance().getPlaceholderAPIConfig().useAdventureReplacer()) {
|
if (PlaceholderAPIPlugin.getInstance().getPlaceholderAPIConfig().useAdventureReplacer()) {
|
||||||
return component.replaceText(config -> config.match(PlaceholderAPI.BRACKET_PLACEHOLDER_PATTERN).replacement((result, builder) ->
|
return component.replaceText(config -> config.match(PlaceholderAPI.BRACKET_PLACEHOLDER_PATTERN).replacement((result, builder) ->
|
||||||
builder.content(BRACKET_EXACT_REPLACER.apply(result.group(), player, PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()::getExpansion))));
|
builder.content(BRACKET_EXACT_REPLACER.apply(result.group(), player, PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()::getExpansion))));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ComponentReplacer.replace(component, str -> PlaceholderAPI.setBracketPlaceholders(player, str));
|
return ComponentReplacer.replace(component, str -> PlaceholderAPI.setBracketPlaceholders(player, str), deserializer == null ? null : s -> deserializer.apply(s).asComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
* @param components List of Components to set the placeholder values in
|
* @param components List of Components to set the placeholder values in
|
||||||
* @return List of Components containing all translated placeholders
|
* @return List of Components containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static List<Component> setBracketPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components) {
|
public static List<Component> setBracketPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components) {
|
||||||
return components.stream().map(component -> setBracketPlaceholders(player, component)).collect(Collectors.toList());
|
return setBracketPlaceholders(player, components, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param components List of Components to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return List of Components containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static List<Component> setBracketPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
|
return components.stream().map(component -> setBracketPlaceholders(player, component, deserializer)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
* @param component Component to set the placeholder values in
|
* @param component Component to set the placeholder values in
|
||||||
* @return Component containing all translated placeholders
|
* @return Component containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Component setBracketPlaceholders(final Player player, @NotNull final Component component) {
|
public static Component setBracketPlaceholders(final Player player, @NotNull final Component component) {
|
||||||
return setBracketPlaceholders((OfflinePlayer) player, component);
|
return setBracketPlaceholders(player, component, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates all placeholders into their corresponding values.
|
* Translates all placeholders into their corresponding values.
|
||||||
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
*
|
*
|
||||||
* @param player Player to parse the placeholders against
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param component Component to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return Component containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Component setBracketPlaceholders(final Player player, @NotNull final Component component, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
|
return setBracketPlaceholders((OfflinePlayer) player, component, deserializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
* @param components List of Components to set the placeholder values in
|
* @param components List of Components to set the placeholder values in
|
||||||
* @return List of Components containing all translated placeholders
|
* @return List of Components containing all translated placeholders
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static List<Component> setBracketPlaceholders(final Player player, @NotNull final List<Component> components) {
|
public static List<Component> setBracketPlaceholders(final Player player, @NotNull final List<Component> components) {
|
||||||
return setBracketPlaceholders((OfflinePlayer) player, components);
|
return setBracketPlaceholders(player, components, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates all placeholders into their corresponding values.
|
||||||
|
* <br>The pattern of a valid placeholder is {@literal {<identifier>_<params>}}.
|
||||||
|
*
|
||||||
|
* @param player Player to parse the placeholders against
|
||||||
|
* @param components List of Components to set the placeholder values in
|
||||||
|
* @param deserializer Optional function to serialize parsed placeholder values into ComponentLike
|
||||||
|
* @return List of Components containing all translated placeholders
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static List<Component> setBracketPlaceholders(final Player player, @NotNull final List<Component> components, @Nullable Function<String, ComponentLike> deserializer) {
|
||||||
|
return setBracketPlaceholders((OfflinePlayer) player, components, deserializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set relational placeholders in the text specified placeholders are matched with the pattern
|
* set relational placeholders in the text specified placeholders are matched with the pattern
|
||||||
* {@literal %<rel_(identifier)_(params)>%} when set with this method
|
* {@literal %<rel_(identifier)_(params)>%} when set with this method
|
||||||
*
|
*
|
||||||
* @param one First player to compare
|
* @param one First player to compare
|
||||||
* @param two Second player to compare
|
* @param two Second player to compare
|
||||||
* @param component Component to parse the placeholders in
|
* @param component Component to parse the placeholders in
|
||||||
* @return The Component containing the parsed relational placeholders
|
* @return The Component containing the parsed relational placeholders
|
||||||
*/
|
*/
|
||||||
@@ -172,8 +289,8 @@ public final class PAPIComponents {
|
|||||||
* Translate placeholders in the provided List based on the relation of the two provided players.
|
* Translate placeholders in the provided List based on the relation of the two provided players.
|
||||||
* <br>The pattern of a valid placeholder is {@literal %rel_<identifier>_<param>%}.
|
* <br>The pattern of a valid placeholder is {@literal %rel_<identifier>_<param>%}.
|
||||||
*
|
*
|
||||||
* @param one Player to compare
|
* @param one Player to compare
|
||||||
* @param two Player to compare
|
* @param two Player to compare
|
||||||
* @param components List of Components to parse the placeholder values to
|
* @param components List of Components to parse the placeholder values to
|
||||||
* @return The List of Components containing the parsed relational placeholders
|
* @return The List of Components containing the parsed relational placeholders
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import net.kyori.adventure.text.event.DataComponentValue;
|
|||||||
import net.kyori.adventure.text.event.HoverEvent;
|
import net.kyori.adventure.text.event.HoverEvent;
|
||||||
import net.kyori.adventure.text.format.Style;
|
import net.kyori.adventure.text.format.Style;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -17,25 +18,25 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
public class ComponentReplacer {
|
public class ComponentReplacer {
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Component replace(@NotNull final Component component, @NotNull final Function<String, String> replacer) {
|
public static Component replace(@NotNull final Component component, @NotNull final Function<String, String> replacer, @Nullable final Function<String, Component> deserializer) {
|
||||||
return rebuild(component, replacer);
|
return rebuild(component, replacer, deserializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static Component rebuild(@NotNull final Component component, @NotNull final Function<String, String> replacer) {
|
private static Component rebuild(@NotNull final Component component, @NotNull final Function<String, String> replacer, @Nullable final Function<String, Component> deserializer) {
|
||||||
Component rebuilt;
|
Component rebuilt;
|
||||||
|
|
||||||
if (component instanceof TextComponent) {
|
if (component instanceof TextComponent) {
|
||||||
final TextComponent text = (TextComponent) component;
|
final TextComponent text = (TextComponent) component;
|
||||||
final String replaced = replacer.apply(text.content());
|
final String replaced = replacer.apply(text.content());
|
||||||
|
|
||||||
rebuilt = Component.text(replaced);
|
rebuilt = deserializer == null ? Component.text(replaced) : deserializer.apply(replaced);
|
||||||
} else if (component instanceof TranslatableComponent) {
|
} else if (component instanceof TranslatableComponent) {
|
||||||
final TranslatableComponent translatable = (TranslatableComponent) component;
|
final TranslatableComponent translatable = (TranslatableComponent) component;
|
||||||
final List<Component> arguments = new ArrayList<>();
|
final List<Component> arguments = new ArrayList<>();
|
||||||
|
|
||||||
for (final ComponentLike arg : translatable.arguments()) {
|
for (final ComponentLike arg : translatable.arguments()) {
|
||||||
arguments.add(rebuild(arg.asComponent(), replacer));
|
arguments.add(rebuild(arg.asComponent(), replacer, deserializer));
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuilt = Component.translatable(translatable.key(), arguments);
|
rebuilt = Component.translatable(translatable.key(), arguments);
|
||||||
@@ -52,12 +53,12 @@ public class ComponentReplacer {
|
|||||||
rebuilt = Component.empty();
|
rebuilt = Component.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuilt = rebuilt.style(rebuildStyle(component.style(), replacer));
|
rebuilt = rebuilt.style(rebuildStyle(component.style(), replacer, deserializer));
|
||||||
|
|
||||||
if (!component.children().isEmpty()) {
|
if (!component.children().isEmpty()) {
|
||||||
final List<Component> children = new ArrayList<>();
|
final List<Component> children = new ArrayList<>();
|
||||||
for (Component child : component.children()) {
|
for (Component child : component.children()) {
|
||||||
children.add(rebuild(child, replacer));
|
children.add(rebuild(child, replacer, deserializer));
|
||||||
}
|
}
|
||||||
rebuilt = rebuilt.children(children);
|
rebuilt = rebuilt.children(children);
|
||||||
}
|
}
|
||||||
@@ -66,7 +67,7 @@ public class ComponentReplacer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static Style rebuildStyle(@NotNull final Style style, @NotNull final Function<String, String> replacer) {
|
private static Style rebuildStyle(@NotNull final Style style, @NotNull final Function<String, String> replacer, @Nullable final Function<String, Component> deserializer) {
|
||||||
final Style.Builder builder = style.toBuilder();
|
final Style.Builder builder = style.toBuilder();
|
||||||
final ClickEvent click = style.clickEvent();
|
final ClickEvent click = style.clickEvent();
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ public class ComponentReplacer {
|
|||||||
final HoverEvent<?> hover = style.hoverEvent();
|
final HoverEvent<?> hover = style.hoverEvent();
|
||||||
|
|
||||||
if (hover != null) {
|
if (hover != null) {
|
||||||
builder.hoverEvent(rebuildHoverEvent(hover, replacer));
|
builder.hoverEvent(rebuildHoverEvent(hover, replacer, deserializer));
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
@@ -113,11 +114,11 @@ public class ComponentReplacer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static HoverEvent<?> rebuildHoverEvent(@NotNull final HoverEvent<?> hover, @NotNull final Function<String, String> replacer) {
|
private static HoverEvent<?> rebuildHoverEvent(@NotNull final HoverEvent<?> hover, @NotNull final Function<String, String> replacer, @Nullable final Function<String, Component> deserializer) {
|
||||||
final Object value = hover.value();
|
final Object value = hover.value();
|
||||||
|
|
||||||
if (value instanceof Component) {
|
if (value instanceof Component) {
|
||||||
final Component rebuilt = rebuild((Component) value, replacer);
|
final Component rebuilt = rebuild((Component) value, replacer, deserializer);
|
||||||
return HoverEvent.showText(rebuilt);
|
return HoverEvent.showText(rebuilt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ public class ComponentReplacer {
|
|||||||
|
|
||||||
Component rebuiltName = null;
|
Component rebuiltName = null;
|
||||||
if (entity.name() != null) {
|
if (entity.name() != null) {
|
||||||
rebuiltName = rebuild(entity.name(), replacer);
|
rebuiltName = rebuild(entity.name(), replacer, deserializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HoverEvent.showEntity(entity.type(), entity.id(), rebuiltName);
|
return HoverEvent.showEntity(entity.type(), entity.id(), rebuiltName);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
public interface Values {
|
public interface Values {
|
||||||
|
|
||||||
|
String NO_ARGUMENTS_PLACEHOLDER = "%player%";
|
||||||
|
String EMPTY_ARGUMENT_PLACEHOLDER = "%player_%";
|
||||||
String SMALL_TEXT = "My name is %player_name%";
|
String SMALL_TEXT = "My name is %player_name%";
|
||||||
String LARGE_TEXT = "My name is %player_name% and my location is (%player_x%, %player_y%, %player_z%), this placeholder is invalid %server_name%";
|
String LARGE_TEXT = "My name is %player_name% and my location is (%player_x%, %player_y%, %player_z%), this placeholder is invalid %server_name%";
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ public interface Values {
|
|||||||
|
|
||||||
final class MockPlayerPlaceholderExpansion extends PlaceholderExpansion {
|
final class MockPlayerPlaceholderExpansion extends PlaceholderExpansion {
|
||||||
|
|
||||||
|
public static final String EMPTY_ARGUMENT = "Empty Argument";
|
||||||
public static final String PLAYER_X = "10";
|
public static final String PLAYER_X = "10";
|
||||||
public static final String PLAYER_Y = "20";
|
public static final String PLAYER_Y = "20";
|
||||||
public static final String PLAYER_Z = "30";
|
public static final String PLAYER_Z = "30";
|
||||||
@@ -83,6 +86,8 @@ public interface Values {
|
|||||||
return PLAYER_Y;
|
return PLAYER_Y;
|
||||||
case "z":
|
case "z":
|
||||||
return PLAYER_Z;
|
return PLAYER_Z;
|
||||||
|
case "":
|
||||||
|
return EMPTY_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of PlaceholderAPI
|
|
||||||
*
|
|
||||||
* PlaceholderAPI
|
|
||||||
* Copyright (c) 2015 - 2026 PlaceholderAPI Team
|
|
||||||
*
|
|
||||||
* PlaceholderAPI free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* PlaceholderAPI is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package me.clip.placeholderapi.replacer;
|
|
||||||
|
|
||||||
import me.clip.placeholderapi.Values;
|
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
|
||||||
|
|
||||||
public class ReplacerBenchmarks {
|
|
||||||
|
|
||||||
@Benchmark
|
|
||||||
public void measureCharsReplacerSmallText() {
|
|
||||||
Values.CHARS_REPLACER.apply(Values.SMALL_TEXT, null, Values.PLACEHOLDERS::get);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Benchmark
|
|
||||||
public void measureCharsReplacerLargeText() {
|
|
||||||
Values.CHARS_REPLACER.apply(Values.LARGE_TEXT, null, Values.PLACEHOLDERS::get);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,7 @@ import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYE
|
|||||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_X;
|
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_X;
|
||||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Y;
|
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Y;
|
||||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Z;
|
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Z;
|
||||||
|
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.EMPTY_ARGUMENT;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import me.clip.placeholderapi.Values;
|
import me.clip.placeholderapi.Values;
|
||||||
@@ -37,6 +38,18 @@ public final class ReplacerUnitTester {
|
|||||||
Values.CHARS_REPLACER.apply("%player_name%", null, Values.PLACEHOLDERS::get));
|
Values.CHARS_REPLACER.apply("%player_name%", null, Values.PLACEHOLDERS::get));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void charsReplacersDoesNotParsePlaceholdersWithNoArguments() {
|
||||||
|
assertEquals(Values.NO_ARGUMENTS_PLACEHOLDER,
|
||||||
|
Values.CHARS_REPLACER.apply(Values.NO_ARGUMENTS_PLACEHOLDER, null, Values.PLACEHOLDERS::get));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void charsReplacersParsesPlaceholdersWithOneArgumentThatIsEmpty() {
|
||||||
|
assertEquals(EMPTY_ARGUMENT,
|
||||||
|
Values.CHARS_REPLACER.apply(Values.EMPTY_ARGUMENT_PLACEHOLDER, null, Values.PLACEHOLDERS::get));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCharsReplacerProducesExpectedSentence() {
|
void testCharsReplacerProducesExpectedSentence() {
|
||||||
assertEquals(String.format(
|
assertEquals(String.format(
|
||||||
|
|||||||
Reference in New Issue
Block a user