mirror of
https://github.com/MiniPlaceholders/MiniPlaceholders.git
synced 2026-11-05 09:00:20 -05:00
Compare commits
172
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a31d98149 | ||
|
|
ae5a8e235e | ||
|
|
f522456b76 | ||
|
|
de7d14d55d | ||
|
|
c0230efb3b | ||
|
|
3cdb7de9f2 | ||
|
|
9c954834cb | ||
|
|
55e493c013 | ||
|
|
ea725a8121 | ||
|
|
d138d00b26 | ||
|
|
d63397fc96 | ||
|
|
fe6ea73bb0 | ||
|
|
be368a7690 | ||
|
|
b31b91e9b8 | ||
|
|
7d8243711a | ||
|
|
7633ada70e | ||
|
|
da6256def1 | ||
|
|
6d8267684a | ||
|
|
a1d5bb7eed | ||
|
|
161a254613 | ||
|
|
d957269743 | ||
|
|
7ac5aec82d | ||
|
|
9713704352 | ||
|
|
4240fb556c | ||
|
|
5a9ac8af87 | ||
|
|
97b68a3616 | ||
|
|
afde5e6e2c | ||
|
|
8a37136565 | ||
|
|
8d5378ff3e | ||
|
|
40fd7e7873 | ||
|
|
aa47088828 | ||
|
|
31ee16e412 | ||
|
|
8aec3e592f | ||
|
|
88c26dee51 | ||
|
|
659be54911 | ||
|
|
3808d660d9 | ||
|
|
c6ecdacdbc | ||
|
|
25a9b435ff | ||
|
|
d95b9fddac | ||
|
|
78d1b83513 | ||
|
|
56a1e9afca | ||
|
|
ba1438457b | ||
|
|
ad36fab536 | ||
|
|
f2ff2c0bf3 | ||
|
|
fff5acdb7b | ||
|
|
6e28786b09 | ||
|
|
f67690f413 | ||
|
|
cc8646cb4e | ||
|
|
4db80d7583 | ||
|
|
67d480c969 | ||
|
|
8593beccbd | ||
|
|
5d92446a6b | ||
|
|
edc82b2bc9 | ||
|
|
70bd4fee88 | ||
|
|
dbf440b4b3 | ||
|
|
0ffc47a889 | ||
|
|
3f3c3f9ec5 | ||
|
|
bfc05edb26 | ||
|
|
c7c1576fe1 | ||
|
|
e84a484fcd | ||
|
|
6743e35109 | ||
|
|
fe2bdd8165 | ||
|
|
8ce7846aa1 | ||
|
|
7c415f19ac | ||
|
|
771cf33788 | ||
|
|
4edf4180cd | ||
|
|
bb6f2d67c3 | ||
|
|
0c51aeb82d | ||
|
|
c2771a42e2 | ||
|
|
431270e8b5 | ||
|
|
a87226d33b | ||
|
|
98d27e40a1 | ||
|
|
59de135b95 | ||
|
|
f939fac882 | ||
|
|
adde850c52 | ||
|
|
94d97b97eb | ||
|
|
122e338e4e | ||
|
|
32fa6bc38d | ||
|
|
373c061089 | ||
|
|
8addfe3829 | ||
|
|
0e7fe5fec2 | ||
|
|
f0bce37135 | ||
|
|
4bd423bba8 | ||
|
|
2d75b5f36e | ||
|
|
a0fd80f883 | ||
|
|
e846e2989b | ||
|
|
aa0e6a1d51 | ||
|
|
702465f922 | ||
|
|
012a322dca | ||
|
|
c780f1c046 | ||
|
|
51c1c71b7f | ||
|
|
530f0ae96b | ||
|
|
c5fdf94d27 | ||
|
|
855f9ca7ff | ||
|
|
e24c08fad0 | ||
|
|
b1faea8c2d | ||
|
|
ed5005be67 | ||
|
|
1003e666e4 | ||
|
|
c7dde0986a | ||
|
|
167c9101be | ||
|
|
a04b7ef182 | ||
|
|
97b5f2b77e | ||
|
|
32cfd1b0a7 | ||
|
|
9be5a6c676 | ||
|
|
64e634033e | ||
|
|
d66dadadab | ||
|
|
3a78626d80 | ||
|
|
7f8325b605 | ||
|
|
759ca10c4f | ||
|
|
6e5bca56c1 | ||
|
|
c21201e948 | ||
|
|
41a35ce3ce | ||
|
|
31f5f286eb | ||
|
|
ce4c694f87 | ||
|
|
62ab9feaea | ||
|
|
076aa128fd | ||
|
|
e2dba8c7ac | ||
|
|
d40e6e2cd8 | ||
|
|
ecbf3d3ee4 | ||
|
|
b0c980672d | ||
|
|
40890cfbd0 | ||
|
|
beee6c6266 | ||
|
|
b0fb3dc4d8 | ||
|
|
a9c3128698 | ||
|
|
a7a7558fdf | ||
|
|
5adab32ef0 | ||
|
|
36ca8be51b | ||
|
|
6254e29eec | ||
|
|
08e57e8e98 | ||
|
|
a1cdb1a212 | ||
|
|
aa86a36ba1 | ||
|
|
0310f1a260 | ||
|
|
28b56abf3b | ||
|
|
504eff952f | ||
|
|
cb75bcb84b | ||
|
|
a78e988132 | ||
|
|
e5b8b36014 | ||
|
|
0cc1c4bdd6 | ||
|
|
ee4e23d658 | ||
|
|
407807948c | ||
|
|
634011dee0 | ||
|
|
12596860b1 | ||
|
|
1ecabc43d6 | ||
|
|
bae4722945 | ||
|
|
df07b08a45 | ||
|
|
b2c73f521d | ||
|
|
977ba115d0 | ||
|
|
2b2c3ecddf | ||
|
|
c63e59aca6 | ||
|
|
de9b8e78d1 | ||
|
|
922e0dceb0 | ||
|
|
68644fd9cd | ||
|
|
d3e3092ca1 | ||
|
|
af07305052 | ||
|
|
ef2c7d80bc | ||
|
|
be94b7e078 | ||
|
|
901335174c | ||
|
|
2b05d77cf8 | ||
|
|
f4c0a7e100 | ||
|
|
87465740c6 | ||
|
|
0f8144a9fb | ||
|
|
3808fc9d89 | ||
|
|
a7cc73fe7d | ||
|
|
8ac0c398d7 | ||
|
|
25618c0d42 | ||
|
|
9b61e8efa0 | ||
|
|
ad95e239bc | ||
|
|
d18ff640d2 | ||
|
|
7536d32055 | ||
|
|
5dffb9c251 | ||
|
|
e75baceb77 | ||
|
|
16e3d09204 |
No files matched your search
@@ -8,12 +8,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.1.fx-zulu"
|
||||
RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh && sdk install java 21.0.6.fx-zulu"
|
||||
@@ -1,45 +1,30 @@
|
||||
# MiniPlaceholders
|
||||
[](https://github.com/MiniPlaceholders/MiniPlaceholders/actions)
|
||||
[](https://github.com/MiniPlaceholders/MiniPlaceholders/actions)
|
||||
[](https://ci.codemc.io/job/MiniPlaceholders/job/MiniPlaceholders/)
|
||||

|
||||
[](https://discord.gg/5NMMzK5mAn)
|
||||

|
||||

|
||||
|
||||
MiniMessage Component-based Placeholders for Paper, Fabric, Sponge, Folia and Velocity platforms
|
||||
|
||||
|
||||
MiniMessage Component-based Placeholders API for Minecraft Platforms.
|
||||
|
||||
## Compatibility
|
||||
- Paper 1.19.3, 1.19.4+
|
||||
- Folia 1.19.4+
|
||||
- Velocity 3.1.2+
|
||||
- Fabric 1.19.4+
|
||||
- Sponge API 8+
|
||||
|
||||
## Commands
|
||||
|
||||
### Velocity
|
||||
|
||||
- `/vminiplaceholders parse me "[message with placeholders]"`
|
||||
- `/vminiplaceholders parse player [some-player] "[message with placeholders]"`
|
||||
|
||||
### Paper | Folia | Fabric | Krypton
|
||||
|
||||
- `/miniplaceholders <parse|help> player [some-player] "[message with placeholders]`
|
||||
- `/miniplaceholders parse player [some-player] "[message with placeholders]"`
|
||||
|
||||
#### Example:
|
||||
|
||||
- `/miniplaceholders parse me "<player_xp>"`
|
||||
- `/vminiplaceholders parse player 4drian3d "<player_name>"`
|
||||
- Paper 1.21+
|
||||
- Velocity 3.4+
|
||||
- Fabric 1.21.6+
|
||||
- Sponge API 12+
|
||||
|
||||
## User Usage
|
||||
|
||||
Check our user usage wiki [here](https://github.com/MiniPlaceholders/MiniPlaceholders/wiki/User-Getting-Started)
|
||||
Check our user usage wiki [here](https://miniplaceholders.github.io/docs/user-guide/User-Getting-Started)
|
||||
|
||||
## API
|
||||
|
||||
Check the available [Javadocs](https://javadoc.io/doc/io.github.miniplaceholders/miniplaceholders-api)
|
||||
|
||||
Or check the [Developer Wiki](https://github.com/MiniPlaceholders/MiniPlaceholders/wiki/Developer-Getting-Started)
|
||||
Or check the [Developer Wiki](https://miniplaceholders.github.io/docs/developer-guide/Developer-Getting-Started)
|
||||
|
||||
### Java
|
||||
```java
|
||||
@@ -47,9 +32,7 @@ Or check the [Developer Wiki](https://github.com/MiniPlaceholders/MiniPlaceholde
|
||||
class Main {
|
||||
public static void registerExpansion() {
|
||||
final Expansion expansion = Expansion.builder("my-expansion")
|
||||
.filter(Player.class)
|
||||
.audiencePlaceholder("name", (audience, ctx, queue) -> {
|
||||
final Player player = (player) audience;
|
||||
.audiencePlaceholder(Player.class, "name", (player, ctx, queue) -> {
|
||||
return Tag.selfClosingInserting(player.getName());
|
||||
})
|
||||
.globalPlaceholder("tps", (ctx, queue) ->
|
||||
@@ -59,8 +42,8 @@ class Main {
|
||||
expansion.register();
|
||||
|
||||
Player player;
|
||||
final TagResolver playerResolver = MiniPlaceholders.getAudiencePlaceholders(player);
|
||||
player.sendMessage(miniMessage().deserialize("Player Name: <my-expansion_name>", playerResolver));
|
||||
final TagResolver playerResolver = MiniPlaceholders.audiencePlaceholders();
|
||||
player.sendMessage(miniMessage().deserialize("Player Name: <my-expansion_name>", player, playerResolver));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +53,10 @@ class Main {
|
||||
```kotlin
|
||||
fun register() {
|
||||
val expansion = expansion("my-expansion") {
|
||||
audiencePlaceholder("name") { aud, _, _ ->
|
||||
audience<Player>("name") { player, _, _ ->
|
||||
aud.getName().asClosingTag()
|
||||
}
|
||||
globalPlaceholder("tps") { _, _ ->
|
||||
global("tps") { _, _ ->
|
||||
Component.text(Bukkit.getTps()[0]).asInsertingTag()
|
||||
}
|
||||
}
|
||||
@@ -81,7 +64,7 @@ fun register() {
|
||||
expansion.register()
|
||||
|
||||
val player: Player
|
||||
val playerResolver = MiniPlaceholders.getAudiencePlaceholders(player)
|
||||
player.sendMessage(miniMessage().deserialize("Player Name: <my-expansion_name>", playerResolver))
|
||||
val playerResolver = MiniPlaceholders.audiencePlaceholders()
|
||||
player.sendMessage(miniMessage().deserialize("Player Name: <my-expansion_name>", player, playerResolver))
|
||||
}
|
||||
```
|
||||
+58
-9
@@ -1,22 +1,28 @@
|
||||
import java.time.LocalDate
|
||||
|
||||
plugins {
|
||||
id("miniplaceholders.base")
|
||||
alias(libs.plugins.jmh)
|
||||
id("miniplaceholders.publish")
|
||||
`java-library`
|
||||
id("com.vanniktech.maven.publish")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.adventure.api)
|
||||
compileOnly(libs.adventure.minimesssage)
|
||||
compileOnly(libs.adventure.minimessage)
|
||||
compileOnly(libs.adventure.serializer.legacy)
|
||||
compileOnly(projects.miniplaceholdersConnect)
|
||||
compileOnlyApi(libs.unnamedinject)
|
||||
|
||||
testImplementation(platform(libs.junit.bom))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testImplementation(libs.adventure.api)
|
||||
testImplementation(libs.adventure.minimesssage)
|
||||
testImplementation(libs.adventure.minimessage)
|
||||
testImplementation(libs.adventure.serializer.plain)
|
||||
testImplementation(libs.adventure.serializer.legacy)
|
||||
|
||||
compileOnly(libs.jspecify)
|
||||
}
|
||||
|
||||
jmh {
|
||||
@@ -25,20 +31,23 @@ jmh {
|
||||
fork.set(2)
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
}
|
||||
javadoc {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
(options as StandardJavadocDocletOptions).links(
|
||||
val o = options as StandardJavadocDocletOptions
|
||||
o.encoding = Charsets.UTF_8.name()
|
||||
o.links(
|
||||
"https://jd.advntr.dev/api/${libs.versions.adventure.get()}/",
|
||||
"https://jd.advntr.dev/text-minimessage/${libs.versions.adventure.get()}/"
|
||||
)
|
||||
o.tags(
|
||||
"apiNote:a:API Note:",
|
||||
"implSpec:a:Implementation Requirements:",
|
||||
"implNote:a:Implementation Note:",
|
||||
"sinceMinecraft:a:Since Minecraft:"
|
||||
)
|
||||
}
|
||||
compileTestJava {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
@@ -50,3 +59,43 @@ tasks {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mavenPublishing {
|
||||
publishToMavenCentral()
|
||||
signAllPublications()
|
||||
coordinates(project.group as String, "miniplaceholders-api", project.version as String)
|
||||
|
||||
pom {
|
||||
name.set(project.name)
|
||||
description.set(project.description)
|
||||
inceptionYear.set(LocalDate.now().year.toString())
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
licenses {
|
||||
license {
|
||||
name.set("The Apache License, Version 2.0")
|
||||
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("4drian3d")
|
||||
name.set("Adrian Gonzales")
|
||||
email.set("adriangonzalesval@gmail.com")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:https://github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
developerConnection.set("scm:git:ssh://git@github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
}
|
||||
ciManagement {
|
||||
name.set("GitHub Actions")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/actions")
|
||||
}
|
||||
issueManagement {
|
||||
name.set("GitHub")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/issues")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,39 +12,46 @@ import org.openjdk.jmh.annotations.State;
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@State(Scope.Benchmark)
|
||||
public class PlaceholderBenchmark {
|
||||
@Benchmark
|
||||
public void placeholderBench(){
|
||||
BenchAudience player = new BenchAudience("4drian3d");
|
||||
Expansion.Builder expansion = Expansion.builder("benchmark")
|
||||
@Benchmark
|
||||
public void placeholderBench() {
|
||||
BenchAudience player = new BenchAudience("4drian3d");
|
||||
Expansion.Builder expansion = Expansion.builder("benchmark")
|
||||
.audiencePlaceholder(
|
||||
"name", (aud, queue, ctx) -> Tag.selfClosingInserting(Component.text(((BenchAudience)aud).getName()))
|
||||
BenchAudience.class,
|
||||
"name",
|
||||
(aud, queue, ctx) -> Tag.selfClosingInserting(Component.text(aud.getName()))
|
||||
)
|
||||
.audiencePlaceholder(
|
||||
"uuid", (aud, queue, ctx) -> Tag.selfClosingInserting(Component.text(((BenchAudience)aud).getUUID().toString()))
|
||||
BenchAudience.class,
|
||||
"uuid",
|
||||
(aud, queue, ctx) -> Tag.selfClosingInserting(Component.text(aud.getUUID().toString()))
|
||||
)
|
||||
.audiencePlaceholder(
|
||||
"tablistfooter", (aud, queue, ctx) ->Tag.selfClosingInserting(Component.text("footer"))
|
||||
"tablistfooter",
|
||||
(aud, queue, ctx) -> Tag.selfClosingInserting(Component.text("footer"))
|
||||
);
|
||||
|
||||
Expansion expansionBuilded = expansion.build();
|
||||
expansionBuilded.audiencePlaceholders(player);
|
||||
final TagResolver resolvers = MiniPlaceholders.getAudiencePlaceholders(player);
|
||||
Expansion expansionBuilded = expansion.build();
|
||||
expansionBuilded.register();
|
||||
final TagResolver resolvers = MiniPlaceholders.audiencePlaceholders();
|
||||
|
||||
MiniMessage.miniMessage().deserialize("Player Name: <benchmark-name>", resolvers);
|
||||
}
|
||||
MiniMessage.miniMessage().deserialize("Player Name: <benchmark_name>", player, resolvers);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void singleBench(){
|
||||
Expansion.builder("single")
|
||||
.globalPlaceholder("servername", (queue, ctx) -> Tag.selfClosingInserting(Component.text("Peruviankkit")))
|
||||
@Benchmark
|
||||
public void singleBench() {
|
||||
Expansion.builder("single")
|
||||
.globalPlaceholder("servername",
|
||||
(queue, ctx) -> Tag.selfClosingInserting(Component.text("Peruviankkit")))
|
||||
.build()
|
||||
.register();
|
||||
|
||||
TagResolver resolvers = MiniPlaceholders.getGlobalPlaceholders();
|
||||
TagResolver resolvers = MiniPlaceholders.globalPlaceholders();
|
||||
|
||||
MiniMessage.miniMessage().deserialize("Server name: <single-servername>", resolvers);
|
||||
}
|
||||
MiniMessage.miniMessage().deserialize("Server name: <single_servername>", resolvers);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,22 @@
|
||||
package io.github.miniplaceholders.api;
|
||||
|
||||
import io.github.miniplaceholders.api.placeholder.AudiencePlaceholder;
|
||||
import io.github.miniplaceholders.api.placeholder.GlobalPlaceholder;
|
||||
import io.github.miniplaceholders.api.placeholder.RelationalPlaceholder;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import io.github.miniplaceholders.api.resolver.AudienceTagResolver;
|
||||
import io.github.miniplaceholders.api.resolver.GlobalTagResolver;
|
||||
import io.github.miniplaceholders.api.resolver.RelationalTagResolver;
|
||||
import io.github.miniplaceholders.api.types.PlaceholderType;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.builder.AbstractBuilder;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Expansion that contains placeholders
|
||||
@@ -23,199 +25,399 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
* <pre>
|
||||
* Player player = event.getPlayer();
|
||||
* Expansion.Builder builder = Expansion.builder("player")
|
||||
* .audiencePlaceholder("name", (p, queue, ctx) -> Tag.selfClosingInserting(Component.text(((Player)p).getUsername()))
|
||||
* .resolver(Player.class, "name", (p, queue, ctx) -> Tag.selfClosingInserting(Component.text(p.getUsername()))
|
||||
* .build();
|
||||
* Expansion expansion = builder.build();
|
||||
* // You can also call the {@link Expansion#register} method to register
|
||||
* // the {@link Expansion} in the {@link MiniPlaceholders} global Extensions and
|
||||
* // use it in {@link MiniPlaceholders#getAudiencePlaceholders(Audience)} e.g.
|
||||
* TagResolver resolver = expansion.audiencePlaceholder(player);
|
||||
* player.sendMessage(MiniMessage.miniMessage().deserialize("Hello {@literal <luckperms_prefix> <player_name>}", resolver));
|
||||
* // use it in {@link MiniPlaceholders#audiencePlaceholders()} e.g.
|
||||
* TagResolver resolver = expansion.resolver();
|
||||
* player.sendMessage(MiniMessage.miniMessage().deserialize("Hello {@literal <luckperms_prefix> <player_name>}", player, resolver));
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NullMarked
|
||||
public sealed interface Expansion permits ExpansionImpl {
|
||||
/**
|
||||
* Get the expansion name
|
||||
* @return the expansion name
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull String name();
|
||||
/**
|
||||
* Get the expansion name
|
||||
*
|
||||
* @return the expansion name
|
||||
* @since 1.0.0
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Get the author of this expansion
|
||||
*
|
||||
* @return the author or null
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Nullable String author();
|
||||
|
||||
/**
|
||||
* Get the version of this expansion
|
||||
*
|
||||
* @return the version or null
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Nullable String version();
|
||||
|
||||
/**
|
||||
* Get the {@link TagResolver} of an {@link Audience}
|
||||
*
|
||||
* @return A TagResolver with variable placeholders of an Audience
|
||||
* @since 1.0.0
|
||||
*/
|
||||
TagResolver audiencePlaceholders();
|
||||
|
||||
/**
|
||||
* Get the relational placeholders based on two audiences
|
||||
*
|
||||
* <p>The resulting TagResolver will give results according
|
||||
* to the 2 {@link Audience} provided and when called
|
||||
* at any time will retrieve the required values</p>
|
||||
*
|
||||
* @return A TagResolver with variable placeholders between 2 {@link Audience}s
|
||||
* @since 1.0.0
|
||||
*/
|
||||
TagResolver relationalPlaceholders();
|
||||
|
||||
/**
|
||||
* Get global placeholders
|
||||
*
|
||||
* @return the global placeholders
|
||||
* @since 1.0.0
|
||||
*/
|
||||
TagResolver globalPlaceholders();
|
||||
|
||||
/**
|
||||
* Register this expansion
|
||||
*
|
||||
* @throws IllegalStateException if the expansion you are trying to register is already registered
|
||||
* @see Expansion#registered()
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void register();
|
||||
|
||||
/**
|
||||
* Unregisters this expansion
|
||||
*
|
||||
* @throws IllegalStateException if the expansion you are trying to unregister is not registered
|
||||
* @see Expansion#registered()
|
||||
* @since 2.1.0
|
||||
*/
|
||||
void unregister();
|
||||
|
||||
/**
|
||||
* Get the registration status of this expansion
|
||||
*
|
||||
* @return true if the expansion is registered, otherwise false
|
||||
* @since 2.1.0
|
||||
*/
|
||||
boolean registered();
|
||||
|
||||
/**
|
||||
* Get the registered audience placeholders
|
||||
*
|
||||
* @return registered audience placeholders
|
||||
*/
|
||||
Collection<AudiencePlaceholder<?>> registeredAudiencePlaceholders();
|
||||
|
||||
/**
|
||||
* Get the registered relational placeholders
|
||||
*
|
||||
* @return registered relational placeholders
|
||||
*/
|
||||
Collection<RelationalPlaceholder<?>> registeredRelationalPlaceholders();
|
||||
|
||||
/**
|
||||
* Get an Audience Placeholder by name
|
||||
*
|
||||
* <pre>{@code
|
||||
* Expansion expansion = MiniPlaceholders.expansionByName("player")
|
||||
* AudiencePlaceholder<?> placeholder = expansion.audiencePlaceholderByName("name");
|
||||
* }</pre>
|
||||
*
|
||||
* @param name the name of the placeholder to search
|
||||
* @return the desired placeholder or null
|
||||
*/
|
||||
@Nullable AudiencePlaceholder<?> audiencePlaceholderByName(final String name);
|
||||
|
||||
/**
|
||||
* Get a Relational Placeholder by name
|
||||
*
|
||||
* <pre>{@code
|
||||
* Expansion expansion = MiniPlaceholders.expansionByName("expansion")
|
||||
* RelationalPlaceholder<?> placeholder = expansion.relationalPlaceholderByName("placeholder");
|
||||
* }</pre>
|
||||
*
|
||||
* @param name the name of the placeholder to search
|
||||
* @return the desired placeholder or null
|
||||
* @apiNote The name of a relational placeholder does not contain the prefix “rel”,
|
||||
* just the name assigned when it was created
|
||||
*/
|
||||
@Nullable RelationalPlaceholder<?> relationalPlaceholderByName(final String name);
|
||||
|
||||
/**
|
||||
* Get a Global Placeholder by name
|
||||
*
|
||||
* <pre>{@code
|
||||
* Expansion expansion = MiniPlaceholders.expansionByName("player")
|
||||
* GlobalPlaceholder placeholder = expansion.globalPlaceholderByName("name");
|
||||
* }</pre>
|
||||
*
|
||||
* @param name the name of the placeholder to search
|
||||
* @return the desired placeholder or null
|
||||
*/
|
||||
@Nullable GlobalPlaceholder globalPlaceholderByName(final String name);
|
||||
|
||||
/**
|
||||
* Checks whether an audience placeholder with the specified name has been registered in this expansion.
|
||||
*
|
||||
* @param name the name of the placeholder to check
|
||||
* @return true if the placeholder is registered, else false
|
||||
*/
|
||||
default boolean hasAudiencePlaceholder(final String name) {
|
||||
return audiencePlaceholderByName(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a relational placeholder with the specified name has been registered in this expansion.
|
||||
*
|
||||
* @param name the name of the placeholder to check
|
||||
* @return true if the placeholder is registered, else false
|
||||
*/
|
||||
default boolean hasRelationalPlaceholder(final String name) {
|
||||
return relationalPlaceholderByName(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a global placeholder with the specified name has been registered in this expansion.
|
||||
*
|
||||
* @param name the name of the placeholder to check
|
||||
* @return true if the placeholder is registered, else false
|
||||
*/
|
||||
default boolean hasGlobalPlaceholder(final String name) {
|
||||
return globalPlaceholderByName(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a TagResolver that contains all placeholders of a specific type in this expansion.
|
||||
*
|
||||
* @param type the desired type
|
||||
* @return a TagResolver containing all placeholder of a specific type
|
||||
*/
|
||||
TagResolver placeholdersByType(PlaceholderType type);
|
||||
|
||||
/**
|
||||
* Creates a new Expansion Builder
|
||||
*
|
||||
* @param name the expansion name
|
||||
* @return a new expansion builder
|
||||
* @since 1.0.0
|
||||
*/
|
||||
static Expansion.Builder builder(final String name) {
|
||||
return new ExpansionImpl.Builder(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a shorter ToString with the details of this expansion.
|
||||
*
|
||||
* @return a shorter ToString
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@Deprecated(forRemoval = true, since = "3.1.0")
|
||||
default String shortToString() {
|
||||
final StringBuilder builder = new StringBuilder(name()).append('[');
|
||||
if (author() != null) {
|
||||
builder.append("Author: ").append(author());
|
||||
}
|
||||
if (version() != null) {
|
||||
if (author() != null) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append("Version: ").append(version());
|
||||
}
|
||||
builder.append(']');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expansion Builder
|
||||
* <p>Example use:</p>
|
||||
* <pre>
|
||||
* Expansion.Builder builder = Expansion.builder("player");
|
||||
* builder
|
||||
* // Thanks to this filter, a cast can be performed without the probability of a ClassCastException
|
||||
* .filter(Player.class)
|
||||
* .resolver("name", (audience, queue, ctx) -> Tag.selfClosingInserting(Component.text(((Player)audience).getUsername())));
|
||||
* Expansion expansion = builder.build();
|
||||
* </pre>
|
||||
* <p>
|
||||
* then use it:
|
||||
*
|
||||
* <pre>
|
||||
* Player player = event.getPlayer();
|
||||
* TagResolver resolver = expansion.resolver(player);
|
||||
* Component messageReplaced = MiniMessage.deserialize({@link String}, resolver);
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NullMarked
|
||||
interface Builder extends AbstractBuilder<Expansion> {
|
||||
|
||||
/**
|
||||
* Get the {@link TagResolver} of the desired {@link Audience}
|
||||
* @param audience the audience
|
||||
* @return A TagResolver with variable placeholders of an Audience
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull TagResolver audiencePlaceholders(final @NotNull Audience audience);
|
||||
|
||||
/**
|
||||
* Get the relational placeholders based on two audiences
|
||||
* Adds an audience placeholder
|
||||
*
|
||||
* <p>The resulting TagResolver will give results according
|
||||
* to the 2 {@link Audience} provided and when called
|
||||
* at any time will retrieve the required values</p>
|
||||
* @param audience the main audience
|
||||
* @param otherAudience the secondary audience
|
||||
* @return A TagResolver with variable placeholders between 2 {@link Audience}s
|
||||
* <p>This type of Placeholder depends on a specific audience to obtain its values</p>
|
||||
*
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
*
|
||||
* @param targetClass the target class
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param audiencePlaceholder the single placeholder
|
||||
* @param <A> the type of audience that this placeholder supports, must be the same as targetClass
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull TagResolver relationalPlaceholders(final @NotNull Audience audience, final @NotNull Audience otherAudience);
|
||||
<A extends Audience> Builder audiencePlaceholder(
|
||||
final @Nullable Class<A> targetClass,
|
||||
final String key,
|
||||
final AudienceTagResolver<A> audiencePlaceholder
|
||||
);
|
||||
|
||||
/**
|
||||
* Get global placeholders
|
||||
* @return the global placeholders
|
||||
* @since 1.0.0
|
||||
* Adds an audience placeholder
|
||||
*
|
||||
* <p>This type of Placeholder depends on a specific audience to obtain its values</p>
|
||||
*
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
*
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param audiencePlaceholder the single placeholder
|
||||
* @return the {@link Builder} itself
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@NotNull TagResolver globalPlaceholders();
|
||||
default Builder audiencePlaceholder(final String key, final AudienceTagResolver<Audience> audiencePlaceholder) {
|
||||
return this.audiencePlaceholder(null, key, audiencePlaceholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this expansion
|
||||
* @since 1.0.0
|
||||
* @throws IllegalStateException if the expansion you are trying to register is already registered
|
||||
* @see Expansion#registered()
|
||||
*/
|
||||
void register();
|
||||
<A extends Audience> Builder audiencePlaceholder(
|
||||
final Class<A> targetClass,
|
||||
final AudiencePlaceholder.Builder.Provider<A> builderProvider
|
||||
);
|
||||
|
||||
/**
|
||||
* Unregisters this expansion
|
||||
* @since 2.1.0
|
||||
* @throws IllegalStateException if the expansion you are trying to unregister is not registered
|
||||
* @see Expansion#registered()
|
||||
*/
|
||||
void unregister();
|
||||
|
||||
/**
|
||||
* Get the registration status of this expansion
|
||||
* @return true if the expansion is registered, otherwise false
|
||||
* @since 2.1.0
|
||||
*/
|
||||
boolean registered();
|
||||
|
||||
/**
|
||||
* Creates a new Expansion Builder
|
||||
* @param name the expansion name
|
||||
* @return a new expansion builder
|
||||
* @since 1.0.0
|
||||
*/
|
||||
static @NotNull Expansion.Builder builder(final @NotNull String name){
|
||||
return new ExpansionImpl.Builder(name);
|
||||
default Builder audiencePlaceholder(final AudiencePlaceholder.Builder.Provider<Audience> builderProvider) {
|
||||
return audiencePlaceholder(Audience.class, builderProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expansion Builder
|
||||
* <p>Example use:</p>
|
||||
* <pre>
|
||||
* Expansion.Builder builder = Expansion.builder("player");
|
||||
* builder
|
||||
* // Thanks to this filter, a cast can be performed without the probability of a ClassCastException
|
||||
* .filter(Player.class)
|
||||
* .audiencePlaceholder("name", (audience, queue, ctx) -> Tag.selfClosingInserting(Component.text(((Player)audience).getUsername())));
|
||||
* Expansion expansion = builder.build();
|
||||
* </pre>
|
||||
* Adds a Relational Placeholder based on two audiences
|
||||
*
|
||||
* then use it:
|
||||
* <p>This type of placeholder allows you to create
|
||||
* components based on a 2-audiences relationship,
|
||||
* one is the one on which the placeholder
|
||||
* is based and the other is the one on which
|
||||
* the placeholder is displayed.</p>
|
||||
*
|
||||
* <pre>
|
||||
* Player player = event.getPlayer();
|
||||
* TagResolver resolver = expansion.audiencePlaceholder(player);
|
||||
* Component messageReplaced = MiniMessage.deserialize({@link String}, resolver);
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.0.0
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
*
|
||||
* @param targetClass the target class
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param relationalPlaceholder the relational placeholder
|
||||
* @param <A> the type of audience that this placeholder supports, must be the same as targetClass
|
||||
* @return the {@link Builder} itself
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface Builder extends AbstractBuilder<Expansion> {
|
||||
/**
|
||||
* Adds an audience placeholder
|
||||
*
|
||||
* <p>This type of Placeholder depends on a specific audience to obtain its values</p>
|
||||
*
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
*
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param audiencePlaceholder the single placeholder
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull Builder audiencePlaceholder(final @NotNull String key, final @NotNull AudiencePlaceholder audiencePlaceholder);
|
||||
<A extends Audience> Builder relationalPlaceholder(
|
||||
final @Nullable Class<A> targetClass,
|
||||
final String key,
|
||||
final RelationalTagResolver<A> relationalPlaceholder
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a Relational Placeholder based on two audiences
|
||||
*
|
||||
* <p>This type of placeholder allows you to create
|
||||
* components based on a 2-audiences relationship,
|
||||
* one is the one on which the placeholder
|
||||
* is based and the other is the one on which
|
||||
* the placeholder is displayed.</p>
|
||||
*
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
*
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param relationalPlaceholder the relational placeholder
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull Builder relationalPlaceholder(final @NotNull String key, final @NotNull RelationalPlaceholder relationalPlaceholder);
|
||||
|
||||
/**
|
||||
* Adds a global placeholder
|
||||
*
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param function the function
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull Builder globalPlaceholder(final @NotNull String key, final @NotNull BiFunction<@NotNull ArgumentQueue, @NotNull Context, @Nullable Tag> function);
|
||||
|
||||
/**
|
||||
* Adds a global placeholder
|
||||
*
|
||||
* <p>This placeholder is not cached and is immutable</p>
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param tag the tag of this placeholder
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@NotNull Builder globalPlaceholder(final @NotNull String key, final @NotNull Tag tag);
|
||||
|
||||
/**
|
||||
* Filter the type of Audiences that this expansion can receive
|
||||
* <p>In case the {@link Expansion#audiencePlaceholders(Audience)} or
|
||||
* {@link Expansion#relationalPlaceholders(Audience, Audience)} method is called
|
||||
* and the provided audiences are not instances of
|
||||
* the specified class, a {@link TagResolver#empty} will be returned</p>
|
||||
*
|
||||
* <p>This eliminates the need to perform a manual
|
||||
* <pre>if(!(audience instanceof Player)) return TagResolver.empty();</pre>
|
||||
*
|
||||
* @param clazz the class to filter
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("_ -> this")
|
||||
@NotNull Builder filter(final @Nullable Class<? extends Audience> clazz);
|
||||
|
||||
/**
|
||||
* Filters the audiences that this expansion can receive through a Predicate
|
||||
* <p>Example:</p>
|
||||
* <pre>
|
||||
* Expansion.builder("example")
|
||||
* .filter(aud -> aud instanceof Player player{@literal &&} isInProtectedServer(player)
|
||||
* .audiencePlaceholder("hello", (aud, queue, ctx) -> Tag.selfInsertingClosing(Component.text("you are in protected server")))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @param predicate the check to realize
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("_ -> this")
|
||||
@NotNull Builder filter(final @Nullable Predicate<@NotNull Audience> predicate);
|
||||
/**
|
||||
* Adds a Relational Placeholder based on two audiences
|
||||
*
|
||||
* <p>This type of placeholder allows you to create
|
||||
* components based on a 2-audiences relationship,
|
||||
* one is the one on which the placeholder
|
||||
* is based and the other is the one on which
|
||||
* the placeholder is displayed.</p>
|
||||
*
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
*
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param relationalResolver the relational placeholder
|
||||
* @return the {@link Builder} itself
|
||||
* @since 3.0.0
|
||||
*/
|
||||
default Builder relationalPlaceholder(
|
||||
final String key,
|
||||
final RelationalTagResolver<Audience> relationalResolver
|
||||
) {
|
||||
return this.relationalPlaceholder(null, key, relationalResolver);
|
||||
}
|
||||
|
||||
<A extends Audience> Builder relationalPlaceholder(
|
||||
final Class<A> targetClass,
|
||||
final RelationalPlaceholder.Builder.Provider<A> builderProvider
|
||||
);
|
||||
|
||||
default Builder relationalPlaceholder(final RelationalPlaceholder.Builder.Provider<Audience> builderProvider) {
|
||||
return relationalPlaceholder(Audience.class, builderProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a global placeholder
|
||||
*
|
||||
* <p>The content of this Placeholder is cached
|
||||
* and can mutate depending on when it is invoked</p>
|
||||
*
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param resolver the function
|
||||
* @return the {@link Builder} itself
|
||||
* @since 3.0.0
|
||||
*/
|
||||
Builder globalPlaceholder(final String key, final GlobalTagResolver resolver);
|
||||
|
||||
/**
|
||||
* Adds a global placeholder
|
||||
*
|
||||
* <p>This placeholder is not cached and is immutable</p>
|
||||
*
|
||||
* @param key the placeholder key, cannot be an empty or black string
|
||||
* @param tag the tag of this placeholder
|
||||
* @return the {@link Builder} itself
|
||||
* @since 1.1.0
|
||||
*/
|
||||
default Builder globalPlaceholder(final String key, final Tag tag) {
|
||||
return this.globalPlaceholder(key, (queue, context) -> tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the author of this expansion
|
||||
*
|
||||
* @param author the author of this expansion
|
||||
* @return the {@link Builder} itself
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Contract("_ -> this")
|
||||
Builder author(final @Nullable String author);
|
||||
|
||||
/**
|
||||
* Sets the version of this expansion
|
||||
*
|
||||
* @param version the version of this expansion
|
||||
* @return the {@link Builder} itself
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Contract("_ -> this")
|
||||
Builder version(final @Nullable String version);
|
||||
}
|
||||
}
|
||||
@@ -1,226 +1,313 @@
|
||||
package io.github.miniplaceholders.api;
|
||||
|
||||
import io.github.miniplaceholders.api.placeholder.AudiencePlaceholder;
|
||||
import io.github.miniplaceholders.api.placeholder.RelationalPlaceholder;
|
||||
import io.github.miniplaceholders.api.placeholder.*;
|
||||
import io.github.miniplaceholders.api.resolver.AudienceTagResolver;
|
||||
import io.github.miniplaceholders.api.resolver.GlobalTagResolver;
|
||||
import io.github.miniplaceholders.api.resolver.RelationalTagResolver;
|
||||
import io.github.miniplaceholders.api.types.PlaceholderType;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.intellij.lang.annotations.Subst;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static io.github.miniplaceholders.api.utils.Conditions.nonNullOrEmptyString;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@NullMarked
|
||||
final class ExpansionImpl implements Expansion {
|
||||
private static final Tags.Single[] EMPTY_SINGLE_AUDIENCE = new Tags.Single[0];
|
||||
private static final Tags.Relational[] EMPTY_RELATIONAL_AUDIENCE = new Tags.Relational[0];
|
||||
private final String name;
|
||||
private final Tags.Single[] audiencePlaceholders;
|
||||
private final Tags.Relational[] relationalPlaceholders;
|
||||
private final TagResolver globalPlaceholders;
|
||||
private final Class<? extends Audience> filterClass;
|
||||
private final Predicate<Audience> predicateFilter;
|
||||
private static final AudiencePlaceholder<?>[] EMPTY_SINGLE_AUDIENCE = new AudiencePlaceholder[0];
|
||||
private static final RelationalPlaceholder<?>[] EMPTY_RELATIONAL_AUDIENCE = new RelationalPlaceholder[0];
|
||||
private static final GlobalPlaceholder[] EMPTY_GLOBAL_PLACEHOLDER = new GlobalPlaceholder[0];
|
||||
|
||||
ExpansionImpl(
|
||||
@NotNull final String expansionName,
|
||||
@Nullable final Collection<Tags.Single> audiencePlaceholders,
|
||||
@Nullable final Collection<Tags.Relational> relationalPlaceholders,
|
||||
@Nullable final TagResolver globalPlaceholders,
|
||||
@Nullable final Class<? extends Audience> filterClass,
|
||||
@Nullable final Predicate<Audience> predicateFilter){
|
||||
this.name = expansionName+"-";
|
||||
this.audiencePlaceholders = audiencePlaceholders != null
|
||||
? audiencePlaceholders.toArray(EMPTY_SINGLE_AUDIENCE)
|
||||
: EMPTY_SINGLE_AUDIENCE;
|
||||
this.relationalPlaceholders = relationalPlaceholders != null
|
||||
? relationalPlaceholders.toArray(EMPTY_RELATIONAL_AUDIENCE)
|
||||
: EMPTY_RELATIONAL_AUDIENCE;
|
||||
this.globalPlaceholders = globalPlaceholders;
|
||||
this.filterClass = filterClass;
|
||||
this.predicateFilter = predicateFilter;
|
||||
private final String name;
|
||||
private final PlaceholderTagResolver audiencePlaceholders;
|
||||
private final PlaceholderTagResolver relationalPlaceholders;
|
||||
private final PlaceholderTagResolver globalPlaceholders;
|
||||
@Nullable
|
||||
private final String author;
|
||||
@Nullable
|
||||
private final String version;
|
||||
|
||||
ExpansionImpl(
|
||||
final String expansionName,
|
||||
@Nullable final Collection<AudiencePlaceholder<?>> audiencePlaceholders,
|
||||
@Nullable final Collection<RelationalPlaceholder<?>> relationalPlaceholders,
|
||||
@Nullable final Collection<GlobalPlaceholder> globalPlaceholders,
|
||||
@Nullable final String author,
|
||||
@Nullable final String version
|
||||
) {
|
||||
this.name = expansionName;
|
||||
this.audiencePlaceholders = audiencePlaceholders != null
|
||||
? new PlaceholderTagResolver(audiencePlaceholders.toArray(EMPTY_SINGLE_AUDIENCE))
|
||||
: PlaceholderTagResolver.EMPTY;
|
||||
this.relationalPlaceholders = relationalPlaceholders != null
|
||||
? new PlaceholderTagResolver(relationalPlaceholders.toArray(EMPTY_RELATIONAL_AUDIENCE))
|
||||
: PlaceholderTagResolver.EMPTY;
|
||||
this.globalPlaceholders = globalPlaceholders == null
|
||||
? PlaceholderTagResolver.EMPTY
|
||||
: new PlaceholderTagResolver(globalPlaceholders.toArray(EMPTY_GLOBAL_PLACEHOLDER));
|
||||
this.author = author;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String author() {
|
||||
return this.author;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String version() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagResolver audiencePlaceholders() {
|
||||
if (audiencePlaceholders.placeholders().length == 0) return TagResolver.empty();
|
||||
|
||||
return this.audiencePlaceholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagResolver relationalPlaceholders() {
|
||||
if (relationalPlaceholders.placeholders().length == 0) return TagResolver.empty();
|
||||
|
||||
return this.relationalPlaceholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagResolver globalPlaceholders() {
|
||||
if (globalPlaceholders.placeholders().length == 0) return TagResolver.empty();
|
||||
|
||||
return this.globalPlaceholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
if (this.registered()) {
|
||||
throw new IllegalStateException("Expansion" + this.name + "is already registered");
|
||||
}
|
||||
MiniPlaceholders.expansions.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
if (!this.registered()) {
|
||||
throw new IllegalStateException("Expansion " + this.name + " is not registered");
|
||||
}
|
||||
MiniPlaceholders.expansions.remove(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registered() {
|
||||
return MiniPlaceholders.expansions.contains(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<AudiencePlaceholder<?>> registeredAudiencePlaceholders() {
|
||||
return List.of((AudiencePlaceholder<?>[]) audiencePlaceholders.placeholders());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RelationalPlaceholder<?>> registeredRelationalPlaceholders() {
|
||||
return List.of((RelationalPlaceholder<?>[]) relationalPlaceholders.placeholders());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable AudiencePlaceholder<?> audiencePlaceholderByName(String name) {
|
||||
requireNonNull(name);
|
||||
for (Placeholder audiencePlaceholder : audiencePlaceholders.placeholders()) {
|
||||
if (Objects.equals(audiencePlaceholder.name(), name)) {
|
||||
return (AudiencePlaceholder<?>) audiencePlaceholder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RelationalPlaceholder<?> relationalPlaceholderByName(String name) {
|
||||
requireNonNull(name);
|
||||
for (Placeholder relationalPlaceholder : relationalPlaceholders.placeholders()) {
|
||||
if (Objects.equals(relationalPlaceholder.name(), name)) {
|
||||
return (RelationalPlaceholder<?>) relationalPlaceholder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable GlobalPlaceholder globalPlaceholderByName(String name) {
|
||||
for (Placeholder placeholder : globalPlaceholders.placeholders()) {
|
||||
if (Objects.equals(placeholder.name(), name)) {
|
||||
return (GlobalPlaceholder) placeholder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagResolver placeholdersByType(PlaceholderType type) {
|
||||
return switch (type) {
|
||||
case AUDIENCE -> audiencePlaceholders();
|
||||
case RELATIONAL -> relationalPlaceholders();
|
||||
case GLOBAL -> globalPlaceholders();
|
||||
};
|
||||
}
|
||||
|
||||
@NullUnmarked
|
||||
public static final class Builder implements Expansion.Builder {
|
||||
private final String expansionName;
|
||||
@Subst("server")
|
||||
private final String expansionPrefix;
|
||||
private Set<AudiencePlaceholder<?>> audiencePlaceholders;
|
||||
private Set<RelationalPlaceholder<?>> relationalPlaceholders;
|
||||
private Set<GlobalPlaceholder> globalPlaceholders;
|
||||
private String author;
|
||||
private String version;
|
||||
|
||||
Builder(@NotNull final String name) {
|
||||
this.expansionName = nonNullOrEmptyString(name, "Expansion name");
|
||||
this.expansionPrefix = name.toLowerCase(Locale.ROOT).concat("_");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Audience> Expansion.Builder audiencePlaceholder(
|
||||
final Class<A> targetClass,
|
||||
final @NotNull @Subst("placeholder") String key,
|
||||
final @NotNull AudienceTagResolver<@NotNull A> audiencePlaceholder
|
||||
) {
|
||||
nonNullOrEmptyString(key, "Placeholder key");
|
||||
requireNonNull(audiencePlaceholder, "the audience placeholder cannot be null");
|
||||
|
||||
if (this.audiencePlaceholders == null) this.audiencePlaceholders = new HashSet<>();
|
||||
|
||||
this.audiencePlaceholders.add(AudiencePlaceholder.single(targetClass, expansionPrefix + key, key, audiencePlaceholder));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends Audience> Expansion.Builder audiencePlaceholder(
|
||||
final @NotNull Class<A> targetClass,
|
||||
final @NotNull AudiencePlaceholder.Builder.Provider<A> builderProvider
|
||||
) {
|
||||
requireNonNull(targetClass, "target class");
|
||||
requireNonNull(builderProvider, "builder");
|
||||
|
||||
if (this.audiencePlaceholders == null) this.audiencePlaceholders = new HashSet<>();
|
||||
|
||||
final AudiencePlaceholder.Builder<A> builder = AudiencePlaceholder.builder(targetClass);
|
||||
builderProvider.provide(builder);
|
||||
final AudiencePlaceholder<@NotNull A> placeholder = builder.build(expansionPrefix);
|
||||
|
||||
this.audiencePlaceholders.add(placeholder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String name(){
|
||||
return this.name;
|
||||
public <A extends Audience> @NotNull Builder relationalPlaceholder(
|
||||
@Nullable Class<A> targetClass,
|
||||
@Subst("relation") @NotNull String key,
|
||||
@NotNull RelationalTagResolver<@NotNull A> relationalPlaceholder
|
||||
) {
|
||||
nonNullOrEmptyString(key, "Placeholder key");
|
||||
requireNonNull(relationalPlaceholder, "the relational placeholder cannot be null");
|
||||
|
||||
if (this.relationalPlaceholders == null) this.relationalPlaceholders = new HashSet<>();
|
||||
|
||||
final var relationalTag = RelationalPlaceholder.relational(targetClass, expansionPrefix + "rel_" + key, key, relationalPlaceholder);
|
||||
this.relationalPlaceholders.add(relationalTag);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TagResolver audiencePlaceholders(@NotNull Audience audience) {
|
||||
if (audiencePlaceholders.length == 0) return TagResolver.empty();
|
||||
public <A extends Audience> Expansion.Builder relationalPlaceholder(
|
||||
final @NotNull Class<A> targetClass,
|
||||
final @NotNull RelationalPlaceholder.Builder.Provider<A> builderProvider
|
||||
) {
|
||||
requireNonNull(targetClass, "target class");
|
||||
requireNonNull(builderProvider, "builder");
|
||||
|
||||
requireNonNull(audience, "the audience cannot be null");
|
||||
if (this.relationalPlaceholders == null) this.relationalPlaceholders = new HashSet<>();
|
||||
|
||||
if ((audience = expansionFilter(audience)) == null) return TagResolver.empty();
|
||||
final RelationalPlaceholder.Builder<A> builder = RelationalPlaceholder.builder(targetClass);
|
||||
builderProvider.provide(builder);
|
||||
final RelationalPlaceholder<@NotNull A> placeholder = builder.build(expansionPrefix);
|
||||
|
||||
final TagResolver.Builder placeholders = TagResolver.builder();
|
||||
for (final Tags.Single pl : this.audiencePlaceholders) {
|
||||
placeholders.resolver(pl.of(audience));
|
||||
}
|
||||
return placeholders.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("OverrideOnly")
|
||||
private Audience expansionFilter(final Audience audience) {
|
||||
if (audience == null || filterClass == null && predicateFilter == null) {
|
||||
return audience;
|
||||
}
|
||||
final boolean eligible = filterClass != null && filterClass.isInstance(audience)
|
||||
|| predicateFilter != null && predicateFilter.test(audience);
|
||||
if (eligible) {
|
||||
return audience;
|
||||
}
|
||||
if (audience instanceof final ForwardingAudience.Single forward) {
|
||||
return expansionFilter(forward.audience());
|
||||
}
|
||||
return null;
|
||||
this.relationalPlaceholders.add(placeholder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TagResolver relationalPlaceholders(@NotNull Audience audience, @NotNull Audience otherAudience){
|
||||
if (relationalPlaceholders.length == 0) return TagResolver.empty();
|
||||
public @NotNull Builder globalPlaceholder(
|
||||
@Subst("time") @NotNull final String key,
|
||||
@NotNull final GlobalTagResolver function
|
||||
) {
|
||||
nonNullOrEmptyString(key, "Placeholder key");
|
||||
requireNonNull(function, "the global placeholder cannot be null");
|
||||
|
||||
requireNonNull(audience, "the audience cannot be null");
|
||||
requireNonNull(otherAudience, "the other audience cannot be null");
|
||||
if (this.globalPlaceholders == null) this.globalPlaceholders = new HashSet<>();
|
||||
|
||||
audience = expansionFilter(audience);
|
||||
otherAudience = expansionFilter(otherAudience);
|
||||
if (audience == null || otherAudience == null) {
|
||||
return TagResolver.empty();
|
||||
}
|
||||
|
||||
final TagResolver.Builder placeholders = TagResolver.builder();
|
||||
for (final Tags.Relational pl : this.relationalPlaceholders) {
|
||||
placeholders.resolver(pl.of(audience, otherAudience));
|
||||
}
|
||||
return placeholders.build();
|
||||
final var globalResolver = new GlobalPlaceholder(expansionPrefix + key, key, function);
|
||||
this.globalPlaceholders.add(globalResolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TagResolver globalPlaceholders(){
|
||||
return this.globalPlaceholders;
|
||||
public Expansion.@NotNull Builder author(@Nullable final String author) {
|
||||
this.author = author;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
if (this.registered()) {
|
||||
throw new IllegalStateException("Expansion" + this.name + "is already registered");
|
||||
}
|
||||
MiniPlaceholders.expansions.add(this);
|
||||
public Expansion.@NotNull Builder version(@Nullable final String version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
if (!this.registered()) {
|
||||
throw new IllegalStateException("Expansion " + this.name + " is not registered");
|
||||
}
|
||||
MiniPlaceholders.expansions.remove(this);
|
||||
public @NotNull Expansion build() {
|
||||
return new ExpansionImpl(
|
||||
this.expansionName,
|
||||
this.audiencePlaceholders,
|
||||
this.relationalPlaceholders,
|
||||
this.globalPlaceholders,
|
||||
this.author,
|
||||
this.version
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registered() {
|
||||
return MiniPlaceholders.expansions.contains(this);
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Expansion that)) return false;
|
||||
|
||||
static final class Builder implements Expansion.Builder {
|
||||
private final String expansionName;
|
||||
private Set<Tags.Single> audiencePlaceholders;
|
||||
private Set<Tags.Relational> relationalPlaceholders;
|
||||
private TagResolver.Builder globalPlaceholders;
|
||||
private Class<? extends Audience> filterClass;
|
||||
private Predicate<Audience> predicateFilter;
|
||||
return that.name().equalsIgnoreCase(this.name());
|
||||
}
|
||||
|
||||
Builder(@NotNull final String name){
|
||||
this.expansionName = nonNullOrEmptyString(name, "Expansion name")
|
||||
.toLowerCase(Locale.ROOT).concat("_");
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Builder audiencePlaceholder(@NotNull final String key, @NotNull final AudiencePlaceholder audiencePlaceholder){
|
||||
nonNullOrEmptyString(key, "Placeholder key");
|
||||
requireNonNull(audiencePlaceholder, "the audience placeholder cannot be null");
|
||||
|
||||
if (this.audiencePlaceholders == null) this.audiencePlaceholders = new HashSet<>();
|
||||
|
||||
this.audiencePlaceholders.add(Tags.single(expansionName+key, audiencePlaceholder));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Builder relationalPlaceholder(@NotNull final String key, @NotNull final RelationalPlaceholder relationalPlaceholder){
|
||||
nonNullOrEmptyString(key, "Placeholder key");
|
||||
requireNonNull(relationalPlaceholder, "the relational placeholder cannot be null");
|
||||
|
||||
if (this.relationalPlaceholders == null) this.relationalPlaceholders = new HashSet<>();
|
||||
|
||||
this.relationalPlaceholders.add(Tags.relational(expansionName+"rel_"+key, relationalPlaceholder));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Builder globalPlaceholder(@NotNull final String key, @NotNull final BiFunction<ArgumentQueue, Context, Tag> function){
|
||||
nonNullOrEmptyString(key, "Placeholder key");
|
||||
requireNonNull(function, "the global placeholder cannot be null");
|
||||
|
||||
if (this.globalPlaceholders == null) this.globalPlaceholders = TagResolver.builder();
|
||||
|
||||
this.globalPlaceholders.tag(expansionName+key, function);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Builder globalPlaceholder(@NotNull final String key, @NotNull final Tag tag){
|
||||
nonNullOrEmptyString(key, "Placeholder key");
|
||||
requireNonNull(tag, "the tag cannot be null");
|
||||
|
||||
if (this.globalPlaceholders == null) this.globalPlaceholders = TagResolver.builder();
|
||||
|
||||
this.globalPlaceholders.tag(expansionName+key, tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Builder filter(@Nullable final Class<? extends Audience> clazz) {
|
||||
this.filterClass = clazz;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Builder filter(@Nullable final Predicate<Audience> predicate){
|
||||
this.predicateFilter = predicate;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Expansion build(){
|
||||
return new ExpansionImpl(
|
||||
this.expansionName,
|
||||
this.audiencePlaceholders,
|
||||
this.relationalPlaceholders,
|
||||
this.globalPlaceholders != null ? this.globalPlaceholders.build() : TagResolver.empty(),
|
||||
this.filterClass,
|
||||
this.predicateFilter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Expansion that)) return false;
|
||||
|
||||
return that.name().equalsIgnoreCase(this.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return Objects.hash(name.hashCode());
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExpansionImpl{" +
|
||||
"name='" + name + '\'' +
|
||||
", audiencePlaceholders=" + audiencePlaceholders +
|
||||
", relationalPlaceholders=" + relationalPlaceholders +
|
||||
", globalPlaceholders=" + globalPlaceholders +
|
||||
", author='" + author + '\'' +
|
||||
", version='" + version + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
package io.github.miniplaceholders.api;
|
||||
|
||||
import io.github.miniplaceholders.api.enums.Platform;
|
||||
import io.github.miniplaceholders.api.types.PlaceholderType;
|
||||
import io.github.miniplaceholders.api.types.Platform;
|
||||
import io.github.miniplaceholders.api.types.RelationalAudience;
|
||||
import io.github.miniplaceholders.connect.InternalPlatform;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static io.github.miniplaceholders.api.utils.Resolvers.applyIfNotEmpty;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* MiniPlaceholders
|
||||
* MiniPlaceholders, a component-based placeholders API.
|
||||
*
|
||||
* <p>This class allows you to obtain the {@link TagResolver}
|
||||
* that other plugins provide based on single {@link Audience},
|
||||
@@ -31,15 +35,15 @@ public final class MiniPlaceholders {
|
||||
static final Set<Expansion> expansions = ConcurrentHashMap.newKeySet();
|
||||
|
||||
/**
|
||||
* Get the platform
|
||||
* Get the platform on which MiniPlaceholders is running.
|
||||
*
|
||||
* @return the platform
|
||||
* @since 1.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static @NotNull Platform getPlatform() {
|
||||
public static @NotNull Platform platform() {
|
||||
return switch (InternalPlatform.platform()) {
|
||||
case PAPER -> Platform.PAPER;
|
||||
case VELOCITY -> Platform.VELOCITY;
|
||||
case KRYPTON -> Platform.KRYPTON;
|
||||
case FABRIC -> Platform.FABRIC;
|
||||
case SPONGE -> Platform.SPONGE;
|
||||
};
|
||||
@@ -48,39 +52,38 @@ public final class MiniPlaceholders {
|
||||
/**
|
||||
* Get the global placeholders
|
||||
*
|
||||
* <pre>TagResolver resolver = MiniPlaceholders.getGlobalPlaceholders();
|
||||
* <pre>TagResolver resolver = MiniPlaceholders.globalPlaceholders();
|
||||
* Component messageParsed = MiniMessage.miniMessage().deserialize({@link String}, resolver);</pre>
|
||||
*
|
||||
* @return global placeholders independent of any audience
|
||||
* @see TagResolver
|
||||
* @since 1.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static @NotNull TagResolver getGlobalPlaceholders() {
|
||||
public static @NotNull TagResolver globalPlaceholders() {
|
||||
final TagResolver.Builder builder = TagResolver.builder();
|
||||
for (final Expansion expansion : expansions) {
|
||||
final TagResolver resolver = expansion.globalPlaceholders();
|
||||
if (resolver != TagResolver.empty())
|
||||
builder.resolver(resolver);
|
||||
applyIfNotEmpty(resolver, builder);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TagResolver based on an Audience
|
||||
* Gets the TagResolver that can get data from an Audience.
|
||||
* <br>
|
||||
* The audience is provided at the time of parsing from the respective MiniMessage instance.
|
||||
*
|
||||
* <pre>TagResolver resolver = MiniPlaceholders.getAudiencePlaceholders({@link Audience});
|
||||
* Component messageParsed = MiniMessage.miniMessage().deserialize({@link String}, resolver);</pre>
|
||||
* <pre>TagResolver resolver = MiniPlaceholders.audiencePlaceholders();
|
||||
* Component messageParsed = MiniMessage.miniMessage().deserialize({@link String}, {@link Audience}, resolver);
|
||||
* </pre>
|
||||
*
|
||||
* @param audience the audience
|
||||
* @return {@link TagResolver} with placeholders based on an audience
|
||||
* @since 1.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static @NotNull TagResolver getAudiencePlaceholders(final @NotNull Audience audience) {
|
||||
requireNonNull(audience, "audience cannot be null");
|
||||
|
||||
public static @NotNull TagResolver audiencePlaceholders() {
|
||||
final TagResolver.Builder resolvers = TagResolver.builder();
|
||||
for (Expansion expansion : expansions) {
|
||||
final TagResolver resolver = expansion.audiencePlaceholders(audience);
|
||||
for (final Expansion expansion : expansions) {
|
||||
final TagResolver resolver = expansion.audiencePlaceholders();
|
||||
applyIfNotEmpty(resolver, resolvers);
|
||||
}
|
||||
return resolvers.build();
|
||||
@@ -88,54 +91,53 @@ public final class MiniPlaceholders {
|
||||
|
||||
/**
|
||||
* Get the relational placeholders based on two audiences
|
||||
* <br>
|
||||
* The audiences are provided at the time of parsing
|
||||
* from the respective MiniMessage instance through the use of a {@link RelationalAudience}.
|
||||
*
|
||||
* <pre>TagResolver resolver = MiniPlaceholders.getRelationalPlaceholders({@link Audience}, {@link Audience});
|
||||
* Component messageParsed = MiniMessage.miniMessage().deserialize({@link String}, resolver);</pre>
|
||||
* <pre>{@code
|
||||
* TagResolver resolver = MiniPlaceholders.relationalPlaceholders();
|
||||
* Component parsed = MiniMessage.miniMessage().deserialize(@link String, {@link RelationalAudience}, resolver);
|
||||
* }</pre>
|
||||
*
|
||||
* @param audience an audience
|
||||
* @param otherAudience another audience
|
||||
* @return placeholders based on two audiences
|
||||
* @since 1.0.0
|
||||
* @since 3.0.0
|
||||
* @see RelationalAudience
|
||||
*/
|
||||
public static @NotNull TagResolver getRelationalPlaceholders(final @NotNull Audience audience, final @NotNull Audience otherAudience) {
|
||||
requireNonNull(audience, "audience cannot be null");
|
||||
requireNonNull(otherAudience, "otherAudience cannot be null");
|
||||
|
||||
public static @NotNull TagResolver relationalPlaceholders() {
|
||||
final TagResolver.Builder builder = TagResolver.builder();
|
||||
for (final Expansion expansion : expansions) {
|
||||
applyIfNotEmpty(
|
||||
expansion.relationalPlaceholders(audience, otherAudience),
|
||||
builder
|
||||
);
|
||||
applyIfNotEmpty(expansion.relationalPlaceholders(), builder);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TagResolver based on an Audience and the global placeholders
|
||||
* Get a TagResolver that can obtain data based on a relationship of 2 audiences
|
||||
* and at the same time from the main audience and global placeholders.
|
||||
* <br>
|
||||
* The audience is provided at the time of parsing from the respective MiniMessage instance.
|
||||
*
|
||||
* <pre>
|
||||
* TagResolver resolver = MiniPlaceholders.getAudienceGlobalPlaceholders({@link Audience});
|
||||
* TagResolver resolver = MiniPlaceholders.audienceGlobalPlaceholders();
|
||||
* TagResolver resolver2 = TagResolver.resolver(
|
||||
* MiniPlaceholders.getAudienceGlobalPlaceholders({@link Audience}),
|
||||
* MiniPlaceholders.getGlobalPlaceholders()
|
||||
* MiniPlaceholders.audienceGlobalPlaceholders(),
|
||||
* MiniPlaceholders.globalPlaceholders()
|
||||
* );
|
||||
* // This two resolvers returns the same TagResolver
|
||||
* assertEquals(resolver, resolver2);
|
||||
* Component messageParsed = MiniMessage.miniMessage().deserialize({@link String}, resolver);
|
||||
* Component parsed = MiniMessage.miniMessage().deserialize({@link String}, {@link Audience}, resolver);
|
||||
* </pre>
|
||||
*
|
||||
* @param audience the audience
|
||||
* @return {@link TagResolver} with placeholders based on an audience and the global placeholders
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static @NotNull TagResolver getAudienceGlobalPlaceholders(final @NotNull Audience audience) {
|
||||
requireNonNull(audience, "audience cannot be null");
|
||||
public static @NotNull TagResolver audienceGlobalPlaceholders() {
|
||||
final TagResolver.Builder builder = TagResolver.builder();
|
||||
|
||||
for (final Expansion expansion : expansions) {
|
||||
applyIfNotEmpty(expansion.audiencePlaceholders(audience), builder);
|
||||
applyIfNotEmpty(expansion.audiencePlaceholders(), builder);
|
||||
applyIfNotEmpty(expansion.globalPlaceholders(), builder);
|
||||
}
|
||||
|
||||
@@ -147,30 +149,27 @@ public final class MiniPlaceholders {
|
||||
* and the global placeholders
|
||||
*
|
||||
* <pre>
|
||||
* TagResolver resolver = MiniPlaceholders.getRelationalGlobalPlaceholders({@link Audience}, {@link Audience});
|
||||
* TagResolver resolver = MiniPlaceholders.relationalGlobalPlaceholders();
|
||||
* TagResolver resolver2 = TagResolver.resolver(
|
||||
* MiniPlaceholders.getRelationalPlaceholders(audience1, audience2),
|
||||
* MiniPlaceholders.getAudiencePlaceholders(audience1),
|
||||
* MiniPlaceholders.getGlobalPlaceholders()
|
||||
* MiniPlaceholders.relationalPlaceholders(),
|
||||
* MiniPlaceholders.audiencePlaceholders(),
|
||||
* MiniPlaceholders.globalPlaceholders()
|
||||
* );
|
||||
* // This methods should return the same TagResolver
|
||||
* assertEquals(resolver, resolver2);
|
||||
* Component messageParsed = MiniMessage.miniMessage().deserialize({@link String}, resolver);
|
||||
* Component messageParsed = MiniMessage.miniMessage().deserialize({@link String}, {@link RelationalAudience}, resolver);
|
||||
* </pre>
|
||||
*
|
||||
* @param audience an audience
|
||||
* @param otherAudience another audience
|
||||
* @return the placeholders based on two audiences, placeholders based on the first audience and the global placeholders
|
||||
* @since 1.1.0
|
||||
* @since 3.0.0
|
||||
* @apiNote In the case of audience placeholders, the audience to be used will be the {@link RelationalAudience#audience()}
|
||||
* @see RelationalAudience
|
||||
*/
|
||||
public static @NotNull TagResolver getRelationalGlobalPlaceholders(final @NotNull Audience audience, final @NotNull Audience otherAudience) {
|
||||
requireNonNull(audience, "audience cannot be null");
|
||||
requireNonNull(otherAudience, "otherAudience cannot be null");
|
||||
|
||||
public static @NotNull TagResolver relationalGlobalPlaceholders() {
|
||||
final TagResolver.Builder builder = TagResolver.builder();
|
||||
for (final Expansion expansion : expansions) {
|
||||
applyIfNotEmpty(expansion.audiencePlaceholders(audience), builder);
|
||||
applyIfNotEmpty(expansion.relationalPlaceholders(audience, otherAudience), builder);
|
||||
applyIfNotEmpty(expansion.audiencePlaceholders(), builder);
|
||||
applyIfNotEmpty(expansion.relationalPlaceholders(), builder);
|
||||
applyIfNotEmpty(expansion.globalPlaceholders(), builder);
|
||||
}
|
||||
|
||||
@@ -178,11 +177,33 @@ public final class MiniPlaceholders {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of expansion registered
|
||||
* @return the amount of expansions registered
|
||||
* @since 1.0.0
|
||||
* Get a TagResolver based on the desired placeholder type.
|
||||
* <br>
|
||||
* {@link PlaceholderType#GLOBAL} will return {@link #globalPlaceholders()}
|
||||
* <br>
|
||||
* {@link PlaceholderType#AUDIENCE} will return {@link #audiencePlaceholders()}
|
||||
* <br>
|
||||
* {@link PlaceholderType#RELATIONAL} will return {@link #relationalPlaceholders()}
|
||||
*
|
||||
* @param type the desired type
|
||||
* @return the TagResolvers from the respective type
|
||||
* @see PlaceholderType
|
||||
*/
|
||||
public static int getExpansionCount(){
|
||||
public static TagResolver placeholdersByType(final PlaceholderType type) {
|
||||
return switch (type) {
|
||||
case GLOBAL -> globalPlaceholders();
|
||||
case AUDIENCE -> audiencePlaceholders();
|
||||
case RELATIONAL -> relationalPlaceholders();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of expansion registered.
|
||||
*
|
||||
* @return the amount of expansions registered
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static int expansionCount(){
|
||||
return expansions.size();
|
||||
}
|
||||
|
||||
@@ -193,14 +214,15 @@ public final class MiniPlaceholders {
|
||||
* Expansion expansion = Expansion.builder("example").build();
|
||||
* expansion.register();
|
||||
*
|
||||
* assertThat(MiniPlaceholders.getExpansionByName("example")).isNotNull();
|
||||
* assertThat(MiniPlaceholders.expansionByName("example")).isNotNull();
|
||||
* </pre>
|
||||
*
|
||||
* @param name the name of the required expansion
|
||||
* @return the required expansion, if not present, will return null
|
||||
* @see Expansion#builder(String)
|
||||
* @since 2.1.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static @Nullable Expansion getExpansionByName(final @NotNull String name) {
|
||||
public static @Nullable Expansion expansionByName(final @NotNull String name) {
|
||||
for (final Expansion expansion : expansions) {
|
||||
if (Objects.equals(expansion.name(), name)) {
|
||||
return expansion;
|
||||
@@ -208,4 +230,15 @@ public final class MiniPlaceholders {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain all available registered expansions
|
||||
*
|
||||
* @return all available registered expansions
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Unmodifiable
|
||||
public static Collection<Expansion> expansionsAvailable() {
|
||||
return Collections.unmodifiableSet(expansions);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package io.github.miniplaceholders.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import io.github.miniplaceholders.api.placeholder.AudiencePlaceholder;
|
||||
import io.github.miniplaceholders.api.placeholder.RelationalPlaceholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.ParsingException;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
|
||||
final class Tags {
|
||||
private Tags() {}
|
||||
|
||||
static Relational relational(@NotNull final String name, @NotNull final RelationalPlaceholder relationalPlaceholder){
|
||||
return new Tags.Relational(name, relationalPlaceholder);
|
||||
}
|
||||
|
||||
static Single single(@NotNull final String name, @NotNull final AudiencePlaceholder audiencePlaceholder){
|
||||
return new Single(name, audiencePlaceholder);
|
||||
}
|
||||
|
||||
static final class Relational {
|
||||
private final RelationalPlaceholder relationalPlaceholder;
|
||||
private final String key;
|
||||
|
||||
Relational(final String key, final RelationalPlaceholder relationalPlaceholder){
|
||||
this.relationalPlaceholder = relationalPlaceholder;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
TagResolver of(@NotNull final Audience audience, @NotNull final Audience otherAudience){
|
||||
return new TagResolver() {
|
||||
@Override
|
||||
public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments,
|
||||
@NotNull Context ctx) throws ParsingException {
|
||||
|
||||
return this.has(name)
|
||||
? relationalPlaceholder.tag(audience, otherAudience, arguments, ctx)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(@NotNull String name) {
|
||||
return key.equalsIgnoreCase(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof final Relational that)) return false;
|
||||
return that.key.equalsIgnoreCase(this.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return Objects.hash(key);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Single {
|
||||
private final AudiencePlaceholder audiencePlaceholder;
|
||||
private final String key;
|
||||
|
||||
private Single(@NotNull final String key, @NotNull final AudiencePlaceholder audiencePlaceholder){
|
||||
this.key = key;
|
||||
this.audiencePlaceholder = audiencePlaceholder;
|
||||
}
|
||||
|
||||
TagResolver of(@NotNull final Audience audience){
|
||||
return new TagResolver() {
|
||||
|
||||
@Override
|
||||
public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments,
|
||||
@NotNull Context ctx) throws ParsingException {
|
||||
return this.has(name)
|
||||
? audiencePlaceholder.tag(audience, arguments, ctx)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(@NotNull String name) {
|
||||
return key.equalsIgnoreCase(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof final Single that)) return false;
|
||||
return that.key.equalsIgnoreCase(this.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return Objects.hash(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package io.github.miniplaceholders.api.enums;
|
||||
|
||||
/**Platforms on which MiniPlaceholders can be used */
|
||||
public enum Platform {
|
||||
/**Paper Platform */
|
||||
PAPER,
|
||||
/**Velocity Platform */
|
||||
VELOCITY,
|
||||
/**Krypton Platform */
|
||||
KRYPTON,
|
||||
/**Fabric Platform */
|
||||
FABRIC,
|
||||
/**Sponge Platform */
|
||||
SPONGE
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
/**Plugin enums */
|
||||
package io.github.miniplaceholders.api.enums;
|
||||
+206
-19
@@ -1,27 +1,214 @@
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import io.github.miniplaceholders.api.resolver.AudienceTagResolver;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import net.kyori.adventure.pointer.Pointered;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.TagPattern;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import org.intellij.lang.annotations.Subst;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
/**Audience Placeholder based on an Audience */
|
||||
@FunctionalInterface
|
||||
public interface AudiencePlaceholder {
|
||||
/**
|
||||
* A Tag based on an Audience
|
||||
* @param audience the audience
|
||||
* @param queue the argument queue
|
||||
* @param ctx the context
|
||||
* @return a Tag
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable Tag tag(
|
||||
@NotNull final Audience audience,
|
||||
@NotNull final ArgumentQueue queue,
|
||||
@NotNull final Context ctx
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A placeholder that can be applied based on an audience of the specified type.
|
||||
*
|
||||
* @param targetClass the type of audience from which this placeholder can obtain information
|
||||
* @param targetFilter the filter that will be applied to the audience before being able to evaluate its return tag
|
||||
* @param key the full placeholder key
|
||||
* @param name the name of this placeholder
|
||||
* @param resolver the object responsible for obtaining audience information
|
||||
* @param <A> the parametrized type
|
||||
*/
|
||||
@NullMarked
|
||||
public record AudiencePlaceholder<A extends Audience>(
|
||||
@Nullable Class<A> targetClass,
|
||||
@Nullable Predicate<A> targetFilter,
|
||||
@TagPattern String key,
|
||||
@TagPattern String name,
|
||||
AudienceTagResolver<A> resolver,
|
||||
@Nullable PlaceholderMetadata placeholderMetadata
|
||||
) implements Placeholder {
|
||||
|
||||
/**
|
||||
* A placeholder that can be applied based on an audience of the specified type.
|
||||
*
|
||||
* @param targetClass the type of audience from which this placeholder can obtain information
|
||||
* @param key the full placeholder key
|
||||
* @param name the name of this placeholder
|
||||
* @param resolver the object responsible for obtaining audience information
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public AudiencePlaceholder(
|
||||
final @Nullable Class<A> targetClass,
|
||||
final @TagPattern String key,
|
||||
final @TagPattern String name,
|
||||
final AudienceTagResolver<A> resolver
|
||||
) {
|
||||
this(targetClass, null, key, name, resolver, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AudiencePlaceholder.
|
||||
*
|
||||
* @param targetClass the type of audience from which this placeholder can obtain information
|
||||
* @param key the placeholder key
|
||||
* @param name the name of this placeholder
|
||||
* @param resolver the object responsible for obtaining audience information
|
||||
* @param <A> the parametrized type
|
||||
* @return a new AudiencePlaceholder
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static <A extends Audience> AudiencePlaceholder<A> single(
|
||||
@Nullable Class<A> targetClass,
|
||||
@TagPattern String key,
|
||||
@TagPattern String name,
|
||||
AudienceTagResolver<A> resolver
|
||||
) {
|
||||
return new AudiencePlaceholder<>(
|
||||
targetClass,
|
||||
null,
|
||||
requireNonNull(key),
|
||||
requireNonNull(name),
|
||||
requireNonNull(resolver),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static <A extends Audience> Builder<A> builder(final Class<A> clazz) {
|
||||
requireNonNull(clazz);
|
||||
return new Builder<>(clazz);
|
||||
}
|
||||
|
||||
public static Builder<Audience> builder() {
|
||||
return new Builder<>(Audience.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Tag resolve(final String name, final ArgumentQueue arguments, final Context ctx) {
|
||||
final Pointered targetRaw = ctx.target();
|
||||
if (targetRaw == null) {
|
||||
return null;
|
||||
}
|
||||
if (targetClass == null) {
|
||||
//noinspection unchecked
|
||||
return this.resolveA(name, (A) targetRaw, arguments, ctx);
|
||||
}
|
||||
final @Nullable A audience = forwardingFilter(targetRaw);
|
||||
if (audience == null) {
|
||||
return null;
|
||||
}
|
||||
if (targetFilter != null && !targetFilter.test(audience)) {
|
||||
return null;
|
||||
}
|
||||
return resolveA(name, audience, arguments, ctx);
|
||||
}
|
||||
|
||||
private @Nullable Tag resolveA(String key, A audience, ArgumentQueue queue, Context ctx) {
|
||||
return this.has(key)
|
||||
? resolver.tag(audience, queue, ctx)
|
||||
: null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OverrideOnly")
|
||||
private @Nullable A forwardingFilter(final Pointered source) {
|
||||
//noinspection DataFlowIssue
|
||||
if (targetClass.isInstance(source)) {
|
||||
return targetClass.cast(source);
|
||||
}
|
||||
if (source instanceof final ForwardingAudience.Single forward) {
|
||||
return forwardingFilter(forward.audience());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String name) {
|
||||
return key.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof AudiencePlaceholder<?> that)) return false;
|
||||
return that.key.equalsIgnoreCase(this.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AudiencePlaceholder{" +
|
||||
"key='" + key + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@NullUnmarked
|
||||
public static final class Builder<A extends Audience> {
|
||||
@NotNull
|
||||
private final Class<A> targetClass;
|
||||
@TagPattern
|
||||
private String name;
|
||||
@Nullable
|
||||
private Predicate<A> targetFilter;
|
||||
private AudienceTagResolver<@NotNull A> resolver;
|
||||
@Nullable
|
||||
private PlaceholderMetadata metadata;
|
||||
|
||||
Builder(final @NotNull Class<A> targetClass) {
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
public Builder<A> name(final @NonNull @TagPattern String name) {
|
||||
this.name = requireNonNull(name, "placeholder name");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> targetFilter(final Predicate<A> targetFilter) {
|
||||
this.targetFilter = targetFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> resolver(final @NotNull AudienceTagResolver<@NotNull A> resolver) {
|
||||
this.resolver = requireNonNull(resolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> metadata(final PlaceholderMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull AudiencePlaceholder<@NonNull A> build(final @NotNull @Subst("expansion") String expansionPrefix) {
|
||||
requireNonNull(this.name, "name");
|
||||
requireNonNull(this.resolver, "resolver");
|
||||
return new AudiencePlaceholder<>(
|
||||
targetClass,
|
||||
targetFilter,
|
||||
expansionPrefix+name,
|
||||
name,
|
||||
resolver,
|
||||
metadata
|
||||
);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Provider<A extends Audience> {
|
||||
void provide(Builder<A> builderProvider);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
|
||||
import io.github.miniplaceholders.api.resolver.GlobalTagResolver;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.TagPattern;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A placeholder that can obtain data without the need to provide an {@link net.kyori.adventure.audience.Audience}
|
||||
* or {@link io.github.miniplaceholders.api.types.RelationalAudience}.
|
||||
*
|
||||
* @param key the placeholder key
|
||||
* @param name the placeholder name
|
||||
* @param resolver the object responsible for providing information
|
||||
*/
|
||||
public record GlobalPlaceholder(@TagPattern String key, String name, GlobalTagResolver resolver) implements Placeholder {
|
||||
@Override
|
||||
public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) {
|
||||
return this.has(name) ? resolver.tag(arguments, ctx) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(@NotNull String name) {
|
||||
return key.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof GlobalPlaceholder(var oKey, var __, var ___)) {
|
||||
return Objects.equals(oKey, this.key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GlobalPlaceholder{" +
|
||||
"key='" + key + '\'' +
|
||||
"name=" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Base class of all Placeholders
|
||||
*/
|
||||
@NullMarked
|
||||
public sealed interface Placeholder extends TagResolver permits AudiencePlaceholder, GlobalPlaceholder, RelationalPlaceholder {
|
||||
/**
|
||||
* The key of this placeholder.<br>
|
||||
* For example, the placeholder <pre>{@code <expansion_placeholder}</pre>
|
||||
* will have the key <code>expansion_placeholder</code>
|
||||
*
|
||||
* @return this placeholder's key
|
||||
*/
|
||||
String key();
|
||||
|
||||
/**
|
||||
* The key of this placeholder.<br>
|
||||
* For example, the placeholder <pre>{@code <expansion_placeholder}</pre>
|
||||
* will have the key <code>placeholder</code>
|
||||
*
|
||||
* @return this placeholder's name
|
||||
*/
|
||||
String name();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@NullMarked
|
||||
public record PlaceholderMetadata(String displayName, String description) {
|
||||
public PlaceholderMetadata {
|
||||
requireNonNull(displayName);
|
||||
requireNonNull(description);
|
||||
}
|
||||
|
||||
public static PlaceholderMetadata data(final String displayName, final String description) {
|
||||
return new PlaceholderMetadata(displayName, description);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.ParsingException;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A TagResolver responsible for containing and processing multiple placeholders.
|
||||
* @param placeholders the placeholders to process
|
||||
*/
|
||||
public record PlaceholderTagResolver(Placeholder... placeholders) implements TagResolver {
|
||||
public static final PlaceholderTagResolver EMPTY = new PlaceholderTagResolver();
|
||||
|
||||
@Override
|
||||
public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) throws ParsingException {
|
||||
@Nullable Tag tag;
|
||||
for (final Placeholder placeholder : placeholders) {
|
||||
tag = placeholder.resolve(name, arguments, ctx);
|
||||
if (tag != null) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(final @NotNull String name) {
|
||||
for (final Placeholder placeholder : placeholders) {
|
||||
if (placeholder.has(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+211
-22
@@ -1,29 +1,218 @@
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import io.github.miniplaceholders.api.resolver.RelationalTagResolver;
|
||||
import io.github.miniplaceholders.api.types.RelationalAudience;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import net.kyori.adventure.pointer.Pointered;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.TagPattern;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import org.intellij.lang.annotations.Subst;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
/**Relational Placeholder based on two Audiences*/
|
||||
@FunctionalInterface
|
||||
public interface RelationalPlaceholder {
|
||||
/**
|
||||
* A Tag based on two audiences
|
||||
* @param audience the principal audience
|
||||
* @param otherAudience the another audience
|
||||
* @param queue the argument queue
|
||||
* @param ctx the context
|
||||
* @return a tag
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable Tag tag(
|
||||
@NotNull final Audience audience,
|
||||
@NotNull final Audience otherAudience,
|
||||
@NotNull final ArgumentQueue queue,
|
||||
@NotNull final Context ctx
|
||||
);
|
||||
}
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Relational Placeholder.
|
||||
* <br>
|
||||
* This placeholder is able to obtain data based on a relationship between 2 audiences
|
||||
* using a type-safe filter and a relational resolver.
|
||||
*
|
||||
* @param targetClass the target class, can be null
|
||||
* @param key the key of the entire placeholder
|
||||
* @param name the name of this placeholder
|
||||
* @param resolver the relational resolver,
|
||||
* in charge of obtaining data based on a relationship of 2 audiences
|
||||
* @param <A> the audience type, if not specified, will be a generic {@linkplain Audience}
|
||||
* @apiNote The {@link #key()} method returns the full name of the placeholder,
|
||||
* for example, if the placeholder is {@code <server_name>},
|
||||
* the {@linkplain #key()} method will return {@code server_name}.
|
||||
* @see RelationalTagResolver
|
||||
*/
|
||||
@NullMarked
|
||||
public record RelationalPlaceholder<A extends Audience>(
|
||||
@Nullable Class<A> targetClass,
|
||||
@Nullable Predicate<A> targetFilter,
|
||||
@TagPattern String key,
|
||||
@TagPattern String name,
|
||||
RelationalTagResolver<A> resolver,
|
||||
@Nullable PlaceholderMetadata placeholderMetadata
|
||||
) implements Placeholder {
|
||||
|
||||
public RelationalPlaceholder(
|
||||
@Nullable Class<A> targetClass,
|
||||
@TagPattern String key,
|
||||
@TagPattern String name,
|
||||
RelationalTagResolver<A> resolver
|
||||
) {
|
||||
this(targetClass, null, key, name, resolver, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method capable of creating an instance of a relational placeholder.
|
||||
*
|
||||
* @param targetClass the target class, can be null
|
||||
* @param key the key of the entire placeholder
|
||||
* @param name the name of this placeholder
|
||||
* @param resolver the relational resolver,
|
||||
* in charge of obtaining data based on a relationship of 2 audiences
|
||||
* @param <A> the audience type, if not specified, will be a generic {@linkplain Audience}
|
||||
* @return a new RelationalPlaceholder
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static <A extends Audience> RelationalPlaceholder<A> relational(
|
||||
@Nullable Class<A> targetClass,
|
||||
@TagPattern String key,
|
||||
@TagPattern String name,
|
||||
RelationalTagResolver<A> resolver
|
||||
) {
|
||||
return new RelationalPlaceholder<>(targetClass, requireNonNull(key), name, requireNonNull(resolver));
|
||||
}
|
||||
|
||||
public static <A extends Audience> Builder<A> builder(Class<A> targetClazz) {
|
||||
return new Builder<>(targetClazz);
|
||||
}
|
||||
|
||||
public static Builder<Audience> builder() {
|
||||
return new Builder<>(Audience.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Tag resolve(String name, ArgumentQueue arguments, Context ctx) {
|
||||
final Pointered targetRaw = ctx.target();
|
||||
if (targetRaw == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(targetRaw instanceof RelationalAudience<?>(Audience a, Audience b))) {
|
||||
return null;
|
||||
}
|
||||
if (targetClass == null) {
|
||||
//noinspection unchecked
|
||||
return this.resolveA(name, (A) a, (A) b, arguments, ctx);
|
||||
}
|
||||
final @Nullable A audience = forwardingFilter(a);
|
||||
if (audience == null) {
|
||||
return null;
|
||||
}
|
||||
final @Nullable A relationalAudience = forwardingFilter(b);
|
||||
if (relationalAudience == null) {
|
||||
return null;
|
||||
}
|
||||
if (targetFilter != null && (!targetFilter.test(audience) || !targetFilter.test(relationalAudience))) {
|
||||
return null;
|
||||
}
|
||||
return this.resolveA(name, audience, relationalAudience, arguments, ctx);
|
||||
}
|
||||
|
||||
private @Nullable Tag resolveA(
|
||||
String name,
|
||||
A audience,
|
||||
A relationalAudience,
|
||||
ArgumentQueue arguments,
|
||||
Context ctx
|
||||
) {
|
||||
return this.has(name)
|
||||
? resolver.tag(audience, relationalAudience, arguments, ctx)
|
||||
: null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OverrideOnly")
|
||||
private @Nullable A forwardingFilter(final Pointered source) {
|
||||
//noinspection DataFlowIssue
|
||||
if (targetClass.isInstance(source)) {
|
||||
return targetClass.cast(source);
|
||||
}
|
||||
if (source instanceof final ForwardingAudience.Single forward) {
|
||||
return forwardingFilter(forward.audience());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String name) {
|
||||
return key.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof final RelationalPlaceholder<?> that)) return false;
|
||||
return that.key.equalsIgnoreCase(this.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RelationalPlaceholder{" +
|
||||
"key='" + key + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@NullUnmarked
|
||||
public static final class Builder<A extends Audience> {
|
||||
@NotNull
|
||||
private final Class<A> targetClass;
|
||||
@TagPattern
|
||||
private String name;
|
||||
@Nullable
|
||||
private Predicate<A> targetFilter;
|
||||
private RelationalTagResolver<@NotNull A> resolver;
|
||||
@Nullable
|
||||
private PlaceholderMetadata metadata;
|
||||
|
||||
Builder(final @NotNull Class<A> targetClass) {
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
public Builder<A> name(final @NonNull @TagPattern String name) {
|
||||
this.name = requireNonNull(name, "placeholder name");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> targetFilter(final Predicate<A> targetFilter) {
|
||||
this.targetFilter = targetFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> resolver(final @NotNull RelationalTagResolver<@NotNull A> resolver) {
|
||||
this.resolver = requireNonNull(resolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> metadata(final PlaceholderMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull RelationalPlaceholder<@NonNull A> build(final @NotNull @Subst("expansion") String expansionPrefix) {
|
||||
requireNonNull(this.name, "name");
|
||||
requireNonNull(this.resolver, "resolver");
|
||||
return new RelationalPlaceholder<>(
|
||||
targetClass,
|
||||
targetFilter,
|
||||
expansionPrefix + name,
|
||||
name,
|
||||
resolver,
|
||||
metadata
|
||||
);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Provider<A extends Audience> {
|
||||
void provide(final Builder<A> builderProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
/**
|
||||
* Placeholders available based on information obtained from Audiences
|
||||
* Placeholders Package.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
package io.github.miniplaceholders.api.placeholder;
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.github.miniplaceholders.api.provider;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
|
||||
/**
|
||||
* An expansion provider.
|
||||
*
|
||||
* @apiNote If necessary, you can obtain platform-dependent instances
|
||||
* by adding a non-final field of the {@link PlatformData} class
|
||||
* with the {@link team.unnamed.inject.Inject} annotation to your ExpansionProvider class.
|
||||
*/
|
||||
public interface ExpansionProvider {
|
||||
/**
|
||||
* Provides an expansion.
|
||||
*
|
||||
* @return an expansion
|
||||
* @apiNote If used outside its MiniPlaceholders dynamic expansion loading context,
|
||||
* you should always check that the loading requirements specified in {@link #loadRequirement} are met.
|
||||
*/
|
||||
Expansion provideExpansion();
|
||||
|
||||
/**
|
||||
* Obtain the requirements that must be met for this expansion to load successfully.
|
||||
* @return the load requirements of this expansion provider
|
||||
*/
|
||||
LoadRequirement loadRequirement();
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package io.github.miniplaceholders.api.provider;
|
||||
|
||||
import io.github.miniplaceholders.api.types.Platform;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* This class specifies the conditions that must be met
|
||||
* in order to load an {@link io.github.miniplaceholders.api.Expansion} from an {@link ExpansionProvider}.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@NullMarked
|
||||
public sealed interface LoadRequirement {
|
||||
|
||||
/**
|
||||
* It means that there is no requirement to load an expansion.
|
||||
*
|
||||
* @return a LoadRequirement that does not perform any validation
|
||||
* @since 3.0.0
|
||||
*/
|
||||
static LoadRequirement none() {
|
||||
return NoneLoadRequirement.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requirement for a complement, whether a plugin or mod, to be present in order for an expansion to be loaded.<br>
|
||||
* The ability to provide multiple strings is provided because names may vary on some platforms.
|
||||
* For example, Velocity requires plugin IDs to be lowercase, such as {@code miniplaceholders},
|
||||
* while Paper can contain uppercase letters, such as {@code MiniPlaceholders}.
|
||||
*
|
||||
* @param name the principal name of this complement
|
||||
* @param aliases the platform aliases of a complement
|
||||
* @return a load requirement that checks for an available complement
|
||||
* @since 3.0.0
|
||||
*/
|
||||
static LoadRequirement requiredComplement(final String name, final String... aliases) {
|
||||
requireNonNull(name);
|
||||
requireNonNull(aliases);
|
||||
return new AvailableComplementRequirement(name, aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple requirement, in case an expansion needs to depend on a platform
|
||||
* as well as one or more add-ons that are installed.
|
||||
*
|
||||
* @param requirements the load requirements
|
||||
* @return a load requirement of many load requirements
|
||||
* @since 3.0.0
|
||||
*/
|
||||
static LoadRequirement allOf(LoadRequirement... requirements) {
|
||||
requireNonNull(requirements);
|
||||
assert requirements.length != 0;
|
||||
return new MultiLoadRequirement(requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requirement that the current execution be on a specific platform.
|
||||
*
|
||||
* @param platforms the supported platforms
|
||||
* @return a load requirement that checks for a supported platform
|
||||
* @since 3.0.0
|
||||
*/
|
||||
static LoadRequirement platform(final Platform... platforms) {
|
||||
requireNonNull(platforms);
|
||||
assert platforms.length != 0;
|
||||
|
||||
return new PlatformRequirement(platforms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requirement that a specific class be present.
|
||||
*
|
||||
* @param clazz the specified class.
|
||||
* @return a load requirement that checks for a present class
|
||||
* @since 3.1.0
|
||||
*/
|
||||
static LoadRequirement requiredClass(
|
||||
@Pattern("^([a-z_][a-z0-9_]*)(\\.[a-z_][a-z0-9_]*)*(\\.[A-Z][A-Za-z0-9_]*)$") final String clazz
|
||||
) {
|
||||
requireNonNull(clazz);
|
||||
return new ClassRequirement(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* This requirement checks whether any of the conditions presented are satisfied.
|
||||
*
|
||||
* @param requirements the requerimients
|
||||
* @return a requirement that checks whether a condition is valid
|
||||
* @since 3.1.0
|
||||
*/
|
||||
static LoadRequirement anyOf(LoadRequirement... requirements) {
|
||||
requireNonNull(requirements);
|
||||
assert requirements.length != 0;
|
||||
return new AnyOfRequirement(requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* NoneLoadRequirement
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
enum NoneLoadRequirement implements LoadRequirement {
|
||||
/**
|
||||
* NoneLoadRequirement instance
|
||||
*/
|
||||
INSTANCE
|
||||
}
|
||||
|
||||
/**
|
||||
* AvailableComplementRequirement
|
||||
*
|
||||
* @param name the main complement name
|
||||
* @param platformAliases the complement aliases
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
record AvailableComplementRequirement(String name, String... platformAliases) implements LoadRequirement {
|
||||
|
||||
/**
|
||||
* Checks if a complement is loaded
|
||||
*
|
||||
* @param platformComplementTester the checker predicate
|
||||
* @return is this expansion should load
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public boolean shouldLoad(final Predicate<String> platformComplementTester) {
|
||||
if (platformComplementTester.test(name)) {
|
||||
return true;
|
||||
}
|
||||
for (String alias : platformAliases) {
|
||||
if (platformComplementTester.test(alias)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AvailableComplementRequirement{" +
|
||||
"complement='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MultiLoadRequirement
|
||||
*
|
||||
* @param requirements the load requirements
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
record MultiLoadRequirement(LoadRequirement... requirements) implements LoadRequirement {
|
||||
}
|
||||
|
||||
/**
|
||||
* PlatformLoadRequirement
|
||||
*
|
||||
* @param platforms the platforms supported
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
record PlatformRequirement(Platform... platforms) implements LoadRequirement {
|
||||
@Override
|
||||
public String toString() {
|
||||
final String platforms = Stream.of(this.platforms)
|
||||
.map(Platform::name)
|
||||
.collect(Collectors.joining(", "));
|
||||
return "PlatformRequirement{" +
|
||||
"platforms=" + platforms +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ClassRequirement
|
||||
*
|
||||
* @param clazz a specified class
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@NullMarked
|
||||
@ApiStatus.Internal
|
||||
record ClassRequirement(String clazz) implements LoadRequirement {
|
||||
public boolean shouldLoad() {
|
||||
try {
|
||||
Class.forName(clazz);
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AnyOfLoadRequirement
|
||||
*
|
||||
* @param requirements the requirements
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
record AnyOfRequirement(LoadRequirement... requirements) implements LoadRequirement {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.github.miniplaceholders.api.provider;
|
||||
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
|
||||
/**
|
||||
* Wrapper for platform-dependent instances.
|
||||
*
|
||||
* @param serverInstance The server instance.<br>
|
||||
* In Paper it will be {@code org.bukkit.Server},<br>
|
||||
* in Velocity it will be {@code com.velocitypowered.api.proxy.ProxyServer},<br>
|
||||
* in Sponge it will be {@code org.spongepowered.api.Server},<br>
|
||||
* and in Fabric it will be {@code net.minecraft.server.MinecraftServer}.
|
||||
* @param complementInstance The main class of MiniPlaceholders, either as a plugin or mod.<br>
|
||||
* In Paper it will be {@code org.bukkit.plugin.java.JavaPlugin},<br>
|
||||
* in Velocity it will be {@code io.github.miniplaceholders.velocity.VelocityPlugin},<br>
|
||||
* in Sponge it will be {@code io.github.miniplaceholders.sponge.SpongePlugin},<br>
|
||||
* and in Fabric it will be {@code net.fabricmc.api.ModInitializer}.
|
||||
* @see MiniPlaceholders#platform()
|
||||
*/
|
||||
public record PlatformData(Object serverInstance, Object complementInstance) {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.github.miniplaceholders.api.resolver;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Audience Placeholder based on an Audience
|
||||
*
|
||||
* @param <A> the audience type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@NullMarked
|
||||
public interface AudienceTagResolver<A extends Audience> {
|
||||
/**
|
||||
* A Tag based on an Audience
|
||||
* @param audience the audience
|
||||
* @param queue the argument queue
|
||||
* @param ctx the context
|
||||
* @return a Tag
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Nullable
|
||||
Tag tag(final A audience, final ArgumentQueue queue, final Context ctx);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.github.miniplaceholders.api.resolver;
|
||||
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A GlobalResolver is responsible for providing information regardless of additional conditions such as an audience.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GlobalTagResolver {
|
||||
/**
|
||||
* A global tag.
|
||||
*
|
||||
* @param queue the argument queue
|
||||
* @param context the parsing context
|
||||
* @return a tag
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Nullable
|
||||
Tag tag(@NonNull ArgumentQueue queue, @NonNull Context context);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.github.miniplaceholders.api.resolver;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Relational Placeholder based on two Audiences.
|
||||
*
|
||||
* @param <A> the audiences type
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@NullMarked
|
||||
public interface RelationalTagResolver<A extends Audience> {
|
||||
/**
|
||||
* A Tag based on two audiences
|
||||
*
|
||||
* @param audience the principal audience
|
||||
* @param relational the another audience
|
||||
* @param queue the argument queue
|
||||
* @param ctx the context
|
||||
* @return a tag
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Nullable
|
||||
Tag tag(final A audience, final A relational, final ArgumentQueue queue, final Context ctx);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Resolvers available based on information obtained from Audiences
|
||||
*/
|
||||
package io.github.miniplaceholders.api.resolver;
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.github.miniplaceholders.api.types;
|
||||
|
||||
/**
|
||||
* Type of Placeholder.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public enum PlaceholderType {
|
||||
/**
|
||||
* The global placeholder type.
|
||||
* <br>
|
||||
* It does not require any type of audience to operate.
|
||||
*/
|
||||
GLOBAL,
|
||||
/**
|
||||
* The audience placeholder type.
|
||||
* <br>
|
||||
* Obtains information from a provided audience.
|
||||
*/
|
||||
AUDIENCE,
|
||||
/**
|
||||
* The relational placeholder type.
|
||||
* <br>
|
||||
* Requires 2 audiences to work, these audiences need to be of the same type.
|
||||
*/
|
||||
RELATIONAL
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.github.miniplaceholders.api.types;
|
||||
|
||||
/**
|
||||
* Platforms on which MiniPlaceholders can be used.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum Platform {
|
||||
/**
|
||||
* Paper Platform
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
PAPER,
|
||||
/**
|
||||
* Velocity Platform
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
VELOCITY,
|
||||
/**
|
||||
* Fabric Platform
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
FABRIC,
|
||||
/**
|
||||
* Sponge Platform
|
||||
*
|
||||
* @since 2.2.0
|
||||
*/
|
||||
SPONGE
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.github.miniplaceholders.api.types;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A relationship between 2 Audiences.
|
||||
*
|
||||
* @param audience the principal audience
|
||||
* @param relational the secondary audience, with which the relationship will be evaluated
|
||||
* @param <A> the audiences type
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@NullMarked
|
||||
public record RelationalAudience<A extends Audience>(A audience, A relational) implements ForwardingAudience.Single {
|
||||
public RelationalAudience {
|
||||
requireNonNull(audience);
|
||||
requireNonNull(relational);
|
||||
}
|
||||
/**
|
||||
* Creates a new relation between two audiences.
|
||||
*
|
||||
* @param audience the principal audience
|
||||
* @param relational the secondary audience, with which the relationship will be evaluated
|
||||
* @param <A> the audiences type
|
||||
* @return a new relation
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static <A extends Audience> RelationalAudience<A> from(A audience, A relational) {
|
||||
return new RelationalAudience<>(audience, relational);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/**Plugin enums */
|
||||
package io.github.miniplaceholders.api.types;
|
||||
@@ -8,23 +8,27 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
||||
public final class Components {
|
||||
private Components(){}
|
||||
/**
|
||||
* "True" Component
|
||||
* "True" Component.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static final TextComponent TRUE_COMPONENT = Component.text("true", NamedTextColor.GREEN);
|
||||
/**
|
||||
* False Component
|
||||
* "False" Component.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static final TextComponent FALSE_COMPONENT = Component.text("false", NamedTextColor.RED);
|
||||
|
||||
/**
|
||||
* "Yes" Component
|
||||
* "Yes" Component.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static final TextComponent YES_COMPONENT = Component.text("yes", NamedTextColor.GREEN);
|
||||
/**
|
||||
* "No" Component
|
||||
* "No" Component.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static final TextComponent NO_COMPONENT = Component.text("no", NamedTextColor.RED);
|
||||
|
||||
@@ -5,70 +5,75 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Checks required for the correct operation of the expansions*/
|
||||
/**
|
||||
* Checks required for the correct operation of the expansions
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class Conditions {
|
||||
private Conditions() {}
|
||||
private Conditions() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is null or empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param name the name supplier
|
||||
* @return the string if it is not null or empty
|
||||
* @since 1.0.0
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
*/
|
||||
public static String nonNullOrEmptyString(@Nullable String string, @NotNull Supplier<@NotNull String> name){
|
||||
if (string == null)
|
||||
throw new NullPointerException(name.get() + " cannot be null");
|
||||
if (string.isBlank())
|
||||
throw new IllegalStateException(name.get() + "cannot be an empty string");
|
||||
return string;
|
||||
}
|
||||
/**
|
||||
* Checks if a string is null or empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param name the name supplier
|
||||
* @return the string if it is not null or empty
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String nonNullOrEmptyString(@Nullable String string, @NotNull Supplier<@NotNull String> name) {
|
||||
if (string == null)
|
||||
throw new NullPointerException(name.get() + " cannot be null");
|
||||
if (string.isBlank())
|
||||
throw new IllegalStateException(name.get() + "cannot be an empty string");
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is null or empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param name the name
|
||||
* @return the string if it is not null or empty
|
||||
* @since 2.2.0
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
*/
|
||||
public static String nonNullOrEmptyString(@Nullable String string, @NotNull String name){
|
||||
if (string == null)
|
||||
throw new NullPointerException(name + " cannot be null");
|
||||
if (string.isBlank())
|
||||
throw new IllegalStateException(name + "cannot be an empty string");
|
||||
return string;
|
||||
}
|
||||
/**
|
||||
* Checks if a string is null or empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param name the name
|
||||
* @return the string if it is not null or empty
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public static String nonNullOrEmptyString(@Nullable String string, @NotNull String name) {
|
||||
if (string == null)
|
||||
throw new NullPointerException(name + " cannot be null");
|
||||
if (string.isBlank())
|
||||
throw new IllegalStateException(name + "cannot be an empty string");
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param reason the reason supplier
|
||||
* @return the string if it not empty
|
||||
* @since 1.0.0
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
*/
|
||||
public static String nonEmptyString(@NotNull String string, @NotNull Supplier<@NotNull String> reason){
|
||||
if (string.isBlank()) throw new IllegalStateException(reason.get());
|
||||
return string;
|
||||
}
|
||||
/**
|
||||
* Check if a string is empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param reason the reason supplier
|
||||
* @return the string if it not empty
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String nonEmptyString(@NotNull String string, @NotNull Supplier<@NotNull String> reason) {
|
||||
if (string.isBlank()) throw new IllegalStateException(reason.get());
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param reason the reason
|
||||
* @return the string if it not empty
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public static String nonEmptyString(@NotNull String string, @NotNull String reason) {
|
||||
if (string.isBlank()) throw new IllegalStateException(reason);
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is empty or consists of whitespace characters only.
|
||||
*
|
||||
* @param string the string
|
||||
* @param reason the reason
|
||||
* @return the string if it not empty
|
||||
* @since 2.2.0
|
||||
* @throws IllegalStateException if the provided string is empty
|
||||
*/
|
||||
public static String nonEmptyString(@NotNull String string, @NotNull String reason){
|
||||
if (string.isBlank()) throw new IllegalStateException(reason);
|
||||
return string;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package io.github.miniplaceholders.api.utils;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
|
||||
/**
|
||||
* Legacy String utilities.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@NullMarked
|
||||
public final class LegacyStrings {
|
||||
private LegacyStrings() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy serializer with hex support
|
||||
* <p>Use this as the last available alternative,
|
||||
* it is recommended to use MiniMessage instead of this serializer</p>
|
||||
*/
|
||||
public static final LegacyComponentSerializer LEGACY_HEX_SERIALIZER = LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.hexColors()
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Parse a string with possible legacy Ampersand/Section symbols
|
||||
* <br>
|
||||
* <p><b>
|
||||
* This method should only be used in case a String is provided from a legacy plugin/mod
|
||||
* or legacy source, where it is very likely to get a legacy string.
|
||||
* It is not recommended to use this method regularly in any sense
|
||||
* </b></p>
|
||||
*
|
||||
* @param string the string
|
||||
* @return a parsed string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static Component parsePossibleLegacy(@Nullable String string) {
|
||||
if (string == null || string.isEmpty()) return Component.empty();
|
||||
if (string.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1) {
|
||||
string = string.replace(LegacyComponentSerializer.SECTION_CHAR, LegacyComponentSerializer.AMPERSAND_CHAR);
|
||||
}
|
||||
if (string.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) == -1) {
|
||||
return miniMessage().deserialize(string);
|
||||
}
|
||||
return miniMessage().deserialize(
|
||||
miniMessage().serialize(LEGACY_HEX_SERIALIZER.deserialize(string))
|
||||
.replace("\\<", "<")
|
||||
.replace("\\>", ">")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string with possible legacy Ampersand/Section symbols
|
||||
* using a parsing Context
|
||||
* <p><b>
|
||||
* This method should only be used in case a String is provided from a legacy plugin/mod
|
||||
* or legacy source, where it is very likely to get a legacy string.
|
||||
* It is not recommended to use this method regularly in any sense
|
||||
* </b></p>
|
||||
*
|
||||
* @param string the string
|
||||
* @param context the provided parsing context
|
||||
* @return a parsed string
|
||||
* @since 2.2.1
|
||||
*/
|
||||
public static Component parsePossibleLegacy(@Nullable String string, final Context context) {
|
||||
if (string == null || string.isEmpty()) return Component.empty();
|
||||
if (string.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1) {
|
||||
string = string.replace(LegacyComponentSerializer.SECTION_CHAR, LegacyComponentSerializer.AMPERSAND_CHAR);
|
||||
}
|
||||
if (string.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) == -1) {
|
||||
return context.deserialize(string);
|
||||
}
|
||||
return context.deserialize(
|
||||
miniMessage().serialize(LEGACY_HEX_SERIALIZER.deserialize(string))
|
||||
.replace("\\<", "<")
|
||||
.replace("\\>", ">")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string with possible legacy Ampersand/Section symbols
|
||||
* using the provided resolver
|
||||
* <p><b>
|
||||
* This method should only be used in case a String is provided from a legacy plugin/mod
|
||||
* or legacy source, where it is very likely to get a legacy string.
|
||||
* It is not recommended to use this method regularly in any sense
|
||||
* </b></p>
|
||||
*
|
||||
* @param string the string
|
||||
* @param resolver a resolver
|
||||
* @return a parsed string
|
||||
* @since 2.2.1
|
||||
*/
|
||||
public static Component parsePossibleLegacy(@Nullable String string, final TagResolver resolver) {
|
||||
if (string == null || string.isEmpty()) return Component.empty();
|
||||
if (string.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1) {
|
||||
string = string.replace(LegacyComponentSerializer.SECTION_CHAR, LegacyComponentSerializer.AMPERSAND_CHAR);
|
||||
}
|
||||
if (string.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) == -1) {
|
||||
return miniMessage().deserialize(string, resolver);
|
||||
}
|
||||
return miniMessage().deserialize(
|
||||
miniMessage().serialize(LEGACY_HEX_SERIALIZER.deserialize(string))
|
||||
.replace("\\<", "<")
|
||||
.replace("\\>", ">"),
|
||||
resolver
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided string contains legacy formatting.
|
||||
*
|
||||
* @param string the string input
|
||||
* @return whether the string has legacy content
|
||||
* @see LegacyComponentSerializer#AMPERSAND_CHAR
|
||||
* @see LegacyComponentSerializer#SECTION_CHAR
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public static boolean hasLegacyFormat(final String string) {
|
||||
requireNonNull(string, "string");
|
||||
return string.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1
|
||||
|| string.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the input to a type depending on whether it contains legacy formatting or not.
|
||||
*
|
||||
* @param string the string input
|
||||
* @param legacyMapping the mapping function in case the string contains legacy formatting
|
||||
* @param modernMapping the mapping function in case the string does not contain any legacy character type,
|
||||
* being safe to use with MiniMessage
|
||||
* @param <R> the result type
|
||||
* @return the mapped result
|
||||
* @see LegacyComponentSerializer#AMPERSAND_CHAR
|
||||
* @see LegacyComponentSerializer#SECTION_CHAR
|
||||
* @see #hasLegacyFormat(String)
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public static <R> R mapLegacyOrElse(
|
||||
final String string,
|
||||
final Function<String, R> legacyMapping,
|
||||
final Function<String, R> modernMapping
|
||||
) {
|
||||
requireNonNull(legacyMapping, "legacyMapping");
|
||||
requireNonNull(modernMapping, "modernInput");
|
||||
if (hasLegacyFormat(string)) {
|
||||
return legacyMapping.apply(string);
|
||||
} else {
|
||||
return modernMapping.apply(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package io.github.miniplaceholders.api.utils;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
|
||||
/**
|
||||
* Legacy utils
|
||||
*/
|
||||
public final class LegacyUtils {
|
||||
private LegacyUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy serializer with hex support
|
||||
* <p>Use this as the last available alternative,
|
||||
* it is recommended to use MiniMessage instead of this serializer</p>
|
||||
*/
|
||||
public static final LegacyComponentSerializer LEGACY_HEX_SERIALIZER = LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.hexColors()
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Parse a string with possible legacy Ampersand/Section symbols
|
||||
* <br>
|
||||
* <p><b>
|
||||
* This method should only be used in case a String is provided from a legacy plugin/mod
|
||||
* or legacy source, where it is very likely to get a legacy string.
|
||||
* It is not recommended to use this method regularly in any sense
|
||||
* </b></p>
|
||||
*
|
||||
* @param string the string
|
||||
* @return a parsed string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Component parsePossibleLegacy(@Nullable String string) {
|
||||
if (string == null || string.isEmpty()) return Component.empty();
|
||||
if (string.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1) {
|
||||
string = string.replace(LegacyComponentSerializer.SECTION_CHAR, LegacyComponentSerializer.AMPERSAND_CHAR);
|
||||
}
|
||||
if (string.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) == -1) {
|
||||
return miniMessage().deserialize(string);
|
||||
}
|
||||
return miniMessage().deserialize(
|
||||
miniMessage().serialize(LEGACY_HEX_SERIALIZER.deserialize(string))
|
||||
.replace("\\<", "<")
|
||||
.replace("\\>", ">")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string with possible legacy Ampersand/Section symbols
|
||||
* using a parsing Context
|
||||
* <p><b>
|
||||
* This method should only be used in case a String is provided from a legacy plugin/mod
|
||||
* or legacy source, where it is very likely to get a legacy string.
|
||||
* It is not recommended to use this method regularly in any sense
|
||||
* </b></p>
|
||||
*
|
||||
* @param string the string
|
||||
* @param context the provided parsing context
|
||||
* @return a parsed string
|
||||
* @since 2.2.1
|
||||
*/
|
||||
public static @NotNull Component parsePossibleLegacy(@Nullable String string, @NotNull final Context context) {
|
||||
if (string == null || string.isEmpty()) return Component.empty();
|
||||
if (string.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1) {
|
||||
string = string.replace(LegacyComponentSerializer.SECTION_CHAR, LegacyComponentSerializer.AMPERSAND_CHAR);
|
||||
}
|
||||
if (string.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) == -1) {
|
||||
return context.deserialize(string);
|
||||
}
|
||||
return context.deserialize(
|
||||
miniMessage().serialize(LEGACY_HEX_SERIALIZER.deserialize(string))
|
||||
.replace("\\<", "<")
|
||||
.replace("\\>", ">")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string with possible legacy Ampersand/Section symbols
|
||||
* using the provided resolver
|
||||
* <p><b>
|
||||
* This method should only be used in case a String is provided from a legacy plugin/mod
|
||||
* or legacy source, where it is very likely to get a legacy string.
|
||||
* It is not recommended to use this method regularly in any sense
|
||||
* </b></p>
|
||||
*
|
||||
* @param string the string
|
||||
* @param resolver a resolver
|
||||
* @return a parsed string
|
||||
* @since 2.2.1
|
||||
*/
|
||||
public static @NotNull Component parsePossibleLegacy(@Nullable String string, @NotNull final TagResolver resolver) {
|
||||
if (string == null || string.isEmpty()) return Component.empty();
|
||||
if (string.indexOf(LegacyComponentSerializer.SECTION_CHAR) != -1) {
|
||||
string = string.replace(LegacyComponentSerializer.SECTION_CHAR, LegacyComponentSerializer.AMPERSAND_CHAR);
|
||||
}
|
||||
if (string.indexOf(LegacyComponentSerializer.AMPERSAND_CHAR) == -1) {
|
||||
return miniMessage().deserialize(string, resolver);
|
||||
}
|
||||
return miniMessage().deserialize(
|
||||
miniMessage().serialize(LEGACY_HEX_SERIALIZER.deserialize(string))
|
||||
.replace("\\<", "<")
|
||||
.replace("\\>", ">"),
|
||||
resolver
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,49 @@
|
||||
package io.github.miniplaceholders.api.utils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Class of utilities to manage TagResolvers
|
||||
* Class of utilities to manage TagResolvers.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@NullMarked
|
||||
public final class Resolvers {
|
||||
private Resolvers() {}
|
||||
|
||||
/**
|
||||
* Check if the TagResolver is an instance of TagResolver.empty(),
|
||||
* that is, it will always return null
|
||||
* that is, it will always return null.
|
||||
*
|
||||
* @param resolver the resolver to check
|
||||
* @return true if the TagResolver is the same as TagResolver.empty(), else false
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static boolean isEmpty(final @NotNull TagResolver resolver) {
|
||||
public static boolean isEmpty(final TagResolver resolver) {
|
||||
return resolver == TagResolver.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the TagResolver provided is not a TagResolver.empty()
|
||||
* Checks if the TagResolver provided is not a TagResolver.empty().
|
||||
*
|
||||
* @param resolver the resolver to check
|
||||
* @return false if the TagResolver is the same as TagResolver.empty(), else true
|
||||
* @since 1.2.0
|
||||
*
|
||||
*/
|
||||
public static boolean isNotEmpty(final @NotNull TagResolver resolver) {
|
||||
public static boolean isNotEmpty(final TagResolver resolver) {
|
||||
return resolver != TagResolver.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a TagResolver to a TagResolver.Builder in case it is not a TagResolver.empty()
|
||||
* Applies a TagResolver to a TagResolver.Builder in case it is not a TagResolver.empty().
|
||||
*
|
||||
* @param resolver the resolver to check
|
||||
* @param builder the resolver that can be added to the builder
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static void applyIfNotEmpty(final @NotNull TagResolver resolver, final @NotNull TagResolver.Builder builder) {
|
||||
public static void applyIfNotEmpty(final TagResolver resolver, final TagResolver.Builder builder) {
|
||||
if (isNotEmpty(resolver)) {
|
||||
builder.resolver(resolver);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package io.github.miniplaceholders.api.utils;
|
||||
|
||||
import io.github.miniplaceholders.api.resolver.AudienceTagResolver;
|
||||
import io.github.miniplaceholders.api.resolver.GlobalTagResolver;
|
||||
import io.github.miniplaceholders.api.resolver.RelationalTagResolver;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
/**
|
||||
* Tags basic instances.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public final class Tags {
|
||||
private Tags() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty Tag
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static final Tag EMPTY_TAG = Tag.selfClosingInserting(Component.empty());
|
||||
|
||||
/**
|
||||
* Null Tag
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static final Tag NULL_TAG = null;
|
||||
|
||||
/**
|
||||
* An Audience placeholder that returns a null Tag
|
||||
*
|
||||
* @param <A> the placeholder supported type
|
||||
* @return an empty audience resolver
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static <A extends Audience> AudienceTagResolver<@NonNull A> emptyAudienceResolver() {
|
||||
return (audience, queue, ctx) -> NULL_TAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Relational placeholder that returns a null tag
|
||||
*
|
||||
* @param <A> the audience type supported
|
||||
* @return an empty relational resolver
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static <A extends Audience> RelationalTagResolver<@NonNull A> emptyRelationalResolver() {
|
||||
return (audience, relational, queue, ctx) -> NULL_TAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Global placeholder that returns a null tag
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private static final GlobalTagResolver EMPTY_GLOBAL_PLACEHOLDER = (queue, ctx) -> NULL_TAG;
|
||||
|
||||
/**
|
||||
* A Global placeholder that returns a null tag
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @return an empty global placeholder
|
||||
*/
|
||||
public static GlobalTagResolver emptyGlobalPlaceholder() {
|
||||
return EMPTY_GLOBAL_PLACEHOLDER;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package io.github.miniplaceholders.api.utils;
|
||||
|
||||
import io.github.miniplaceholders.api.placeholder.AudiencePlaceholder;
|
||||
import io.github.miniplaceholders.api.placeholder.RelationalPlaceholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
|
||||
/**Tags utils */
|
||||
public final class TagsUtils {
|
||||
private TagsUtils(){}
|
||||
|
||||
/**
|
||||
* Empty Tag
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static final Tag EMPTY_TAG = Tag.selfClosingInserting(Component.empty());
|
||||
|
||||
/**
|
||||
* Null Tag
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static final Tag NULL_TAG = null;
|
||||
|
||||
/**
|
||||
* An Audience placeholder that returns a null Tag
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static final AudiencePlaceholder NULL_AUDIENCE_PLACEHOLDER = (aud, queue, ctx) -> NULL_TAG;
|
||||
|
||||
/**
|
||||
* A Relational placeholder that returns a null tag
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static final RelationalPlaceholder NULL_RELATIONAL_PLACEHOLDER = (aud, otheraud, queue, ctx) -> NULL_TAG;
|
||||
|
||||
/**
|
||||
* Creates a new Tag based on a String
|
||||
* @param string the string
|
||||
* @return a new basic self closing Tag of a string
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull Tag staticTag(@NotNull String string) {
|
||||
return Tag.selfClosingInserting(Component.text(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new basic Tag based on a Component
|
||||
* @param component the component
|
||||
* @return a new basic self closing Tag of a component
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull Tag staticTag(@NotNull Component component) {
|
||||
return Tag.selfClosingInserting(component);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
/** MiniPlaceholders API Module */
|
||||
/**
|
||||
* MiniPlaceholders API Module
|
||||
*/
|
||||
open module io.github.miniplaceholders.api {
|
||||
requires io.github.miniplaceholders.connect;
|
||||
requires io.github.miniplaceholders.connect;
|
||||
|
||||
requires net.kyori.adventure;
|
||||
requires net.kyori.adventure.text.minimessage;
|
||||
requires net.kyori.adventure.text.serializer.legacy;
|
||||
requires net.kyori.examination.api;
|
||||
requires net.kyori.adventure;
|
||||
requires net.kyori.adventure.text.minimessage;
|
||||
requires net.kyori.adventure.text.serializer.legacy;
|
||||
requires net.kyori.examination.api;
|
||||
|
||||
requires static org.jetbrains.annotations;
|
||||
requires static org.jetbrains.annotations;
|
||||
requires static org.jspecify;
|
||||
|
||||
exports io.github.miniplaceholders.api;
|
||||
exports io.github.miniplaceholders.api.enums;
|
||||
exports io.github.miniplaceholders.api.utils;
|
||||
exports io.github.miniplaceholders.api.placeholder;
|
||||
exports io.github.miniplaceholders.api;
|
||||
exports io.github.miniplaceholders.api.types;
|
||||
exports io.github.miniplaceholders.api.utils;
|
||||
exports io.github.miniplaceholders.api.resolver;
|
||||
exports io.github.miniplaceholders.api.placeholder;
|
||||
exports io.github.miniplaceholders.api.provider;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ArgumentTest implements MiniTest {
|
||||
@Test
|
||||
@DisplayName("Empty Arguments")
|
||||
void testEmptyArguments() {
|
||||
final Expansion expansion = Expansion.builder("test")
|
||||
.globalPlaceholder("testing", (queue, ctx) -> {
|
||||
int arguments = 0;
|
||||
while (queue.hasNext()) {
|
||||
arguments++;
|
||||
Tag.Argument argument = queue.pop();
|
||||
assertTrue(argument.value().isBlank());
|
||||
}
|
||||
return Tag.selfClosingInserting(Component.text("Arguments: " + arguments));
|
||||
})
|
||||
.build();
|
||||
|
||||
Component parsed = miniMessage().deserialize("<test_testing::>", expansion.globalPlaceholders());
|
||||
|
||||
assertContentEquals(parsed, Component.text("Arguments: 2"));
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,65 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.utils.TagsUtils;
|
||||
import io.github.miniplaceholders.test.testobjects.TestAudienceHolder;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import io.github.miniplaceholders.api.placeholder.GlobalPlaceholder;
|
||||
import io.github.miniplaceholders.api.types.PlaceholderType;
|
||||
import io.github.miniplaceholders.api.utils.Tags;
|
||||
import io.github.miniplaceholders.test.instances.TestAudience;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static io.github.miniplaceholders.test.PlaceholderTest.assertContentEquals;
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ExpansionsTest {
|
||||
class ExpansionsTest implements MiniTest {
|
||||
@Test
|
||||
@DisplayName("Expansion Equality")
|
||||
void equalsExpansions(){
|
||||
void equalsExpansions() {
|
||||
final Expansion firstExpansion = Expansion.builder("equals")
|
||||
.audiencePlaceholder("test", TagsUtils.NULL_AUDIENCE_PLACEHOLDER)
|
||||
.build();
|
||||
.audiencePlaceholder("test", Tags.emptyAudienceResolver())
|
||||
.build();
|
||||
final Expansion secondExpansion = Expansion.builder("equals")
|
||||
.audiencePlaceholder("test", TagsUtils.NULL_AUDIENCE_PLACEHOLDER)
|
||||
.build();
|
||||
.audiencePlaceholder("test", Tags.emptyAudienceResolver())
|
||||
.build();
|
||||
|
||||
assertEquals(firstExpansion, secondExpansion);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullForwardedAudience() {
|
||||
TestAudienceHolder audience = new TestAudienceHolder(null);
|
||||
|
||||
void placeholderRetrievalTest() {
|
||||
final Expansion expansion = Expansion.builder("test")
|
||||
.filter(aud -> aud.toString().isBlank())
|
||||
.audiencePlaceholder("testing", (aud, queue, ctx) -> Tag.selfClosingInserting(Component.text(aud.toString())))
|
||||
.globalPlaceholder("some_placeholder", Tag.preProcessParsed("test"))
|
||||
.build();
|
||||
|
||||
Component result = assertDoesNotThrow(
|
||||
() -> miniMessage().deserialize("<test_testing>", expansion.audiencePlaceholders(audience))
|
||||
);
|
||||
assertTrue(expansion.hasGlobalPlaceholder("some_placeholder"));
|
||||
final GlobalPlaceholder somePlaceholder = expansion.globalPlaceholderByName("some_placeholder");
|
||||
assertNotNull(somePlaceholder);
|
||||
|
||||
assertContentEquals(result, Component.text("<test_testing>"));
|
||||
final var expansionResult = parse("Result: <test_some_placeholder>", expansion.globalPlaceholders());
|
||||
final var placeholderResult = parse("Result: <test_some_placeholder>", somePlaceholder);
|
||||
assertSimilarity("Result: test", expansionResult);
|
||||
assertSimilarity("Result: test", placeholderResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeholdersByType() {
|
||||
final Expansion expansion = Expansion.builder("idk")
|
||||
.audiencePlaceholder(TestAudience.class, "test", Tags.emptyAudienceResolver())
|
||||
.build();
|
||||
|
||||
var placeholders = expansion.placeholdersByType(PlaceholderType.AUDIENCE);
|
||||
assertNotNull(placeholders);
|
||||
assertEquals(placeholders, expansion.audiencePlaceholders());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeholdersByName() {
|
||||
final Expansion expansion = Expansion.builder("test")
|
||||
.globalPlaceholder("someexpansion", Tags.emptyGlobalPlaceholder())
|
||||
.build();
|
||||
|
||||
final GlobalPlaceholder placeholder = expansion.globalPlaceholderByName("someexpansion");
|
||||
assertNotNull(placeholder);
|
||||
assertEquals("someexpansion", placeholder.name());
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,42 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import io.github.miniplaceholders.api.utils.LegacyUtils;
|
||||
import io.github.miniplaceholders.api.utils.LegacyStrings;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.Inserting;
|
||||
import net.kyori.adventure.text.minimessage.tag.PreProcess;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class LegacyTest {
|
||||
class LegacyTest implements MiniTest {
|
||||
@Test
|
||||
void testLegacyParsing() {
|
||||
final String string = "<rainbow>hello... i &8hate legacy format §bso much";
|
||||
final Component parsed = LegacyUtils.parsePossibleLegacy(string);
|
||||
final Component parsed = LegacyStrings.parsePossibleLegacy(string);
|
||||
final String result = PlainTextComponentSerializer.plainText().serialize(parsed);
|
||||
final String expected = "hello... i hate legacy format so much";
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMapLegacyOrElse() {
|
||||
final String legacyInput = "&fhi <red>aaaa";
|
||||
Tag legacyProcessedTag = LegacyStrings.mapLegacyOrElse(
|
||||
legacyInput,
|
||||
legacy -> Tag.inserting(LegacyStrings.parsePossibleLegacy(legacy)),
|
||||
Tag::preProcessParsed
|
||||
);
|
||||
assertInstanceOf(Inserting.class, legacyProcessedTag);
|
||||
|
||||
final String modernInput = "<rainbow>aaaaaaaaaaaaa";
|
||||
Tag modernProcessedTag = LegacyStrings.mapLegacyOrElse(
|
||||
modernInput,
|
||||
__ -> fail(),
|
||||
Tag::preProcessParsed
|
||||
);
|
||||
assertInstanceOf(PreProcess.class, modernProcessedTag);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,28 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import io.github.miniplaceholders.api.utils.TagsUtils;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import io.github.miniplaceholders.api.utils.Tags;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MiniPlaceholdersTest {
|
||||
class MiniPlaceholdersTest implements MiniTest {
|
||||
@Test
|
||||
@Disabled("Cannot correctly calculate the equity of 2 TagResolvers obtained in different ways")
|
||||
void methodEquality(){
|
||||
Expansion.builder("equality")
|
||||
.audiencePlaceholder("audience", TagsUtils.NULL_AUDIENCE_PLACEHOLDER)
|
||||
.globalPlaceholder("global", TagsUtils.EMPTY_TAG)
|
||||
.audiencePlaceholder("audience", Tags.emptyAudienceResolver())
|
||||
.globalPlaceholder("global", (queue, ctx) -> Tags.EMPTY_TAG)
|
||||
.build()
|
||||
.register();
|
||||
|
||||
|
||||
assertEquals(
|
||||
MiniPlaceholders.getAudienceGlobalPlaceholders(Audience.empty()),
|
||||
MiniPlaceholders.audienceGlobalPlaceholders(),
|
||||
TagResolver.resolver(
|
||||
MiniPlaceholders.getAudiencePlaceholders(Audience.empty()),
|
||||
MiniPlaceholders.getGlobalPlaceholders()
|
||||
MiniPlaceholders.audiencePlaceholders(),
|
||||
MiniPlaceholders.globalPlaceholders()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -37,6 +33,8 @@ class MiniPlaceholdersTest {
|
||||
.build();
|
||||
expansion.register();
|
||||
|
||||
assertNotNull(MiniPlaceholders.expansionByName("testregistration"));
|
||||
|
||||
assertTrue(expansion.registered());
|
||||
assertThrows(IllegalStateException.class, expansion::register);
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public interface MiniTest {
|
||||
default void assertContentEquals(Component first, Component second) {
|
||||
String firstSerialized = PlainTextComponentSerializer.plainText().serialize(first);
|
||||
String secondSerialized = PlainTextComponentSerializer.plainText().serialize(second);
|
||||
|
||||
assertEquals(firstSerialized, secondSerialized);
|
||||
}
|
||||
|
||||
default void assertSimilarity(String string, Component component) {
|
||||
final String serialized = PlainTextComponentSerializer.plainText().serializeOrNull(component);
|
||||
|
||||
assertNotNull(serialized);
|
||||
|
||||
assertEquals(string, serialized);
|
||||
}
|
||||
|
||||
default Component parse(String string, TagResolver resolver) {
|
||||
return MiniMessage.miniMessage().deserialize(string, resolver);
|
||||
}
|
||||
}
|
||||
@@ -1,154 +1,29 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import io.github.miniplaceholders.test.testobjects.TestAudience;
|
||||
import io.github.miniplaceholders.test.testobjects.TestAudienceHolder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PlaceholderTest {
|
||||
class PlaceholderTest implements MiniTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Audience Placeholder Test")
|
||||
void audienceExpansionPlaceholdersTest(){
|
||||
TestAudience player = new TestAudience("4drian3d");
|
||||
|
||||
Expansion expansion = Expansion.builder("example")
|
||||
.audiencePlaceholder("name", (aud, queue, ctx) -> {
|
||||
TestAudience audience = (TestAudience) aud;
|
||||
return Tag.selfClosingInserting(Component.text(audience.name()));
|
||||
}).build();
|
||||
|
||||
final Component expected = Component.text("Player Name: 4drian3d");
|
||||
final Component result = miniMessage().deserialize("Player Name: <example_name>", expansion.audiencePlaceholders(player));
|
||||
|
||||
assertContentEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Relational Placeholder Test")
|
||||
void relationalExpansionPlaceholdersTest(){
|
||||
Audience p1 = new TestAudience("PlayerOne");
|
||||
Audience p2 = new TestAudience("PlayerTwo");
|
||||
|
||||
Expansion expansion = Expansion.builder("relational")
|
||||
.relationalPlaceholder("enemy",
|
||||
(aud, otheraud, queue, ctx) -> Tag.selfClosingInserting(this.isEnemy(((TestAudience)aud),((TestAudience)otheraud))
|
||||
? Component.text("Enemy", NamedTextColor.RED)
|
||||
: Component.text("Neutral", NamedTextColor.GREEN)
|
||||
))
|
||||
.build();
|
||||
|
||||
assertEquals(MiniMessage.miniMessage().deserialize("You are <red>Enemy"), MiniMessage.miniMessage().deserialize("You are <relational_rel_enemy>", expansion.relationalPlaceholders(p1, p2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Global Placeholder Test")
|
||||
void globalExpansionPlaceholderTest(){
|
||||
Expansion expansion = Expansion.builder("global")
|
||||
.globalPlaceholder("players", (queue, ctx) -> Tag.selfClosingInserting(Component.text(1305)))
|
||||
.globalPlaceholder("servers", (queue, ctx) -> Tag.selfClosingInserting(Component.text(7)))
|
||||
.build();
|
||||
|
||||
final Component expected = Component.text("Online players: 1305 | Servers: 7");
|
||||
final Component actual = MiniMessage.miniMessage().deserialize("Online players: <global_players> | Servers: <global_servers>", expansion.globalPlaceholders());
|
||||
|
||||
assertContentEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Registered Placeholders")
|
||||
void instancePlaceholdersTest(){
|
||||
Expansion.builder("instance")
|
||||
.globalPlaceholder("hello", (queue, ctx) -> Tag.selfClosingInserting(Component.text("hello", NamedTextColor.RED)))
|
||||
@Test
|
||||
@DisplayName("Registered Placeholders")
|
||||
void instancePlaceholdersTest() {
|
||||
Expansion.builder("instance")
|
||||
.globalPlaceholder("hello", (queue, ctx) ->
|
||||
Tag.selfClosingInserting(Component.text("hello", NamedTextColor.RED)))
|
||||
.build()
|
||||
.register();
|
||||
|
||||
Component expected = Component.text("hello player", NamedTextColor.RED);
|
||||
Component actual = MiniMessage.miniMessage().deserialize("<instance_hello> player", MiniPlaceholders.getGlobalPlaceholders());
|
||||
Component expected = Component.text("hello player", NamedTextColor.RED);
|
||||
Component actual = MiniMessage.miniMessage().deserialize("<instance_hello> player", MiniPlaceholders.globalPlaceholders());
|
||||
|
||||
assertContentEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Filtered Expansion")
|
||||
void filteredExpansion(){
|
||||
Expansion expansion = Expansion.builder("filter")
|
||||
.audiencePlaceholder("name", (aud, queue, ctx) -> Tag.selfClosingInserting(Component.text(((TestAudience)aud).name())))
|
||||
.filter(TestAudience.class)
|
||||
.build();
|
||||
|
||||
Audience player = new TestAudience("TestPlayer04");
|
||||
Audience emptyAudience = Audience.empty();
|
||||
|
||||
String string = "Player Name: <filter_name>";
|
||||
|
||||
Component playerExpected = Component.text("Player Name: TestPlayer04");
|
||||
Component emptyExpected = Component.text("Player Name: <filter_name>");
|
||||
|
||||
assertContentEquals(playerExpected, MiniMessage.miniMessage().deserialize(string, expansion.audiencePlaceholders(player)));
|
||||
assertContentEquals(emptyExpected, MiniMessage.miniMessage().deserialize(string, expansion.audiencePlaceholders(emptyAudience)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Empty Arguments")
|
||||
void testEmptyArguments() {
|
||||
final Expansion expansion = Expansion.builder("test")
|
||||
.globalPlaceholder("testing", (queue, ctx) -> {
|
||||
int arguments = 0;
|
||||
while (queue.hasNext()) {
|
||||
arguments++;
|
||||
Tag.Argument argument = queue.pop();
|
||||
assertTrue(argument.value().isBlank());
|
||||
}
|
||||
return Tag.selfClosingInserting(Component.text("Arguments: "+arguments));
|
||||
})
|
||||
.build();
|
||||
|
||||
Component parsed = miniMessage().deserialize("<test_testing::>", expansion.globalPlaceholders());
|
||||
|
||||
assertContentEquals(parsed, Component.text("Arguments: 2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Forwarding Audience Parsing Test")
|
||||
void forwardingAudienceTest() {
|
||||
Expansion expansion = Expansion.builder("test")
|
||||
.filter(TestAudience.class)
|
||||
.audiencePlaceholder("testing", (audience, queue, ctx) -> {
|
||||
TestAudience testAudience = (TestAudience) audience;
|
||||
return Tag.selfClosingInserting(Component.text("Name: "+testAudience.name()));
|
||||
}).build();
|
||||
|
||||
TestAudience testAudience = new TestAudience("4drian3d");
|
||||
Audience forward = new TestAudienceHolder(testAudience);
|
||||
|
||||
Component parsed = miniMessage().deserialize("<test_testing>.", expansion.audiencePlaceholders(forward));
|
||||
Component expected = Component.text("Name: 4drian3d.");
|
||||
|
||||
assertContentEquals(parsed, expected);
|
||||
}
|
||||
|
||||
static void assertContentEquals(Component first, Component second){
|
||||
String firstSerialized = PlainTextComponentSerializer.plainText().serialize(first);
|
||||
String secondSerialized = PlainTextComponentSerializer.plainText().serialize(second);
|
||||
|
||||
assertEquals(firstSerialized, secondSerialized);
|
||||
}
|
||||
|
||||
private boolean isEnemy(TestAudience a1, TestAudience a2){
|
||||
return a1.name().equals("PlayerOne") && a2.name().equals("PlayerTwo");
|
||||
}
|
||||
assertContentEquals(expected, actual);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Modifying;
|
||||
import net.kyori.adventure.text.minimessage.tag.ParserDirective;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TagTest implements MiniTest {
|
||||
@Test
|
||||
void testTag() {
|
||||
Expansion.builder("tags")
|
||||
.globalPlaceholder("modifying", FilteringTag::new)
|
||||
.globalPlaceholder("parser_directive", ParserDirective.RESET)
|
||||
.build()
|
||||
.register();
|
||||
|
||||
final TagResolver globalResolver = MiniPlaceholders.globalPlaceholders();
|
||||
assertSimilarity("------ replacement ------", parse("------ <tags_modifying>test</tags_modifying> ------", globalResolver));
|
||||
assertEquals(Component.text()
|
||||
.append(Component.text("aaaaaa", NamedTextColor.RED), Component.text("aaaaaaa"))
|
||||
.build(),
|
||||
parse("<red>aaaaaa<tags_parser_directive>aaaaaaa", globalResolver));
|
||||
}
|
||||
|
||||
public record FilteringTag(ArgumentQueue queue, Context context) implements Modifying {
|
||||
@Override
|
||||
public Component apply(@NotNull Component current, int depth) {
|
||||
if (depth != 0) {
|
||||
return Component.empty();
|
||||
}
|
||||
final String serialized = PlainTextComponentSerializer.plainText().serialize(current);
|
||||
if (serialized.contains("test")) {
|
||||
return Component.text(serialized.replace("test", "replacement"));
|
||||
}
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,17 @@
|
||||
package io.github.miniplaceholders.test;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.resolver.GlobalTagResolver;
|
||||
import io.github.miniplaceholders.api.utils.Tags;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import io.github.miniplaceholders.api.utils.TagsUtils;
|
||||
import net.kyori.adventure.text.minimessage.Context;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
|
||||
|
||||
//yea... it is intended xd
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
class ThrowableTest {
|
||||
class ThrowableTest implements MiniTest {
|
||||
@Test
|
||||
@DisplayName("Nullable Exception")
|
||||
void throwOnNull(){
|
||||
@@ -25,17 +19,8 @@ class ThrowableTest {
|
||||
|
||||
Expansion.Builder expansionTest = assertDoesNotThrow(() -> Expansion.builder("throwable"));
|
||||
|
||||
assertThrows(NullPointerException.class, () -> expansionTest.audiencePlaceholder(null, (aud, queue, ctx) -> TagsUtils.EMPTY_TAG));
|
||||
assertThrows(NullPointerException.class, () -> expansionTest.globalPlaceholder("test", (BiFunction<ArgumentQueue, Context, Tag>)null));
|
||||
|
||||
Expansion.builder("testThrows")
|
||||
.audiencePlaceholder("test", (aud, queue, ctx) -> TagsUtils.EMPTY_TAG)
|
||||
.relationalPlaceholder("test2", (aud, aud2, queue, ctx) -> TagsUtils.EMPTY_TAG)
|
||||
.build()
|
||||
.register();
|
||||
|
||||
assertThrows(NullPointerException.class, () -> MiniPlaceholders.getAudiencePlaceholders(null));
|
||||
assertThrows(NullPointerException.class, () -> MiniPlaceholders.getRelationalPlaceholders(null, null));
|
||||
assertThrows(NullPointerException.class, () -> expansionTest.audiencePlaceholder(null, (aud, queue, ctx) -> Tags.EMPTY_TAG));
|
||||
assertThrows(NullPointerException.class, () -> expansionTest.globalPlaceholder("test", (GlobalTagResolver) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -43,9 +28,9 @@ class ThrowableTest {
|
||||
void throwOnBlankString(){
|
||||
Expansion.Builder builder = assertDoesNotThrow(() -> Expansion.builder("hello"));
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> builder.audiencePlaceholder("", (aud, queue, ctx) -> TagsUtils.EMPTY_TAG));
|
||||
assertThrows(IllegalStateException.class, () -> builder.globalPlaceholder(" ", (queue, ctx) -> TagsUtils.EMPTY_TAG));
|
||||
assertThrows(IllegalStateException.class, () -> builder.audiencePlaceholder("", (aud, queue, ctx) -> Tags.EMPTY_TAG));
|
||||
assertThrows(IllegalStateException.class, () -> builder.globalPlaceholder(" ", (queue, ctx) -> Tags.EMPTY_TAG));
|
||||
|
||||
assertDoesNotThrow(() -> builder.globalPlaceholder("hehe", (queue, ctx) -> TagsUtils.EMPTY_TAG));
|
||||
assertDoesNotThrow(() -> builder.globalPlaceholder("hehe", (queue, ctx) -> Tags.EMPTY_TAG));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.github.miniplaceholders.test.testobjects;
|
||||
package io.github.miniplaceholders.test.instances;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.github.miniplaceholders.test.testobjects;
|
||||
package io.github.miniplaceholders.test.instances;
|
||||
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.github.miniplaceholders.test.loader;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.provider.ExpansionProvider;
|
||||
import io.github.miniplaceholders.api.provider.LoadRequirement;
|
||||
import io.github.miniplaceholders.api.types.Platform;
|
||||
|
||||
public class TestExpansionProvider implements ExpansionProvider {
|
||||
@Override
|
||||
public Expansion provideExpansion() {
|
||||
return Expansion.builder("provided").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadRequirement loadRequirement() {
|
||||
return LoadRequirement.allOf(
|
||||
LoadRequirement.platform(Platform.FABRIC, Platform.PAPER),
|
||||
LoadRequirement.requiredComplement("luckperms", "LuckPerms")
|
||||
);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package io.github.miniplaceholders.test.placeholder;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.placeholder.PlaceholderMetadata;
|
||||
import io.github.miniplaceholders.test.MiniTest;
|
||||
import io.github.miniplaceholders.test.instances.TestAudience;
|
||||
import io.github.miniplaceholders.test.instances.TestAudienceHolder;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
|
||||
public class AudiencePlaceholderTest implements MiniTest {
|
||||
@Test
|
||||
@DisplayName("Audience Placeholder Test")
|
||||
void audienceExpansionPlaceholdersTest() {
|
||||
TestAudience player = new TestAudience("4drian3d");
|
||||
|
||||
Expansion expansion = Expansion.builder("example")
|
||||
.audiencePlaceholder("name", (aud, queue, ctx) -> {
|
||||
TestAudience audience = (TestAudience) aud;
|
||||
return Tag.selfClosingInserting(Component.text(audience.name()));
|
||||
}).build();
|
||||
|
||||
final Component expected = Component.text("Player Name: 4drian3d");
|
||||
final Component result = miniMessage().deserialize("Player Name: <example_name>", player, expansion.audiencePlaceholders());
|
||||
|
||||
assertContentEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Forwarding Audience Parsing Test")
|
||||
void forwardingAudienceTest() {
|
||||
Expansion expansion = Expansion.builder("test")
|
||||
.audiencePlaceholder(TestAudience.class, "testing",
|
||||
(audience, queue, ctx) -> Tag.selfClosingInserting(Component.text("Name: " + audience.name())))
|
||||
.build();
|
||||
|
||||
TestAudience testAudience = new TestAudience("4drian3d");
|
||||
Audience forward = new TestAudienceHolder(testAudience);
|
||||
|
||||
Component parsed = miniMessage().deserialize("<test_testing>.", forward, expansion.audiencePlaceholders());
|
||||
Component expected = Component.text("Name: 4drian3d.");
|
||||
|
||||
assertContentEquals(parsed, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Filtered Expansion")
|
||||
void filteredExpansion() {
|
||||
Expansion expansion = Expansion.builder("filter")
|
||||
.audiencePlaceholder(TestAudience.class, "name",
|
||||
(aud, queue, ctx) -> Tag.selfClosingInserting(Component.text(aud.name())))
|
||||
.build();
|
||||
|
||||
Audience player = new TestAudience("TestPlayer04");
|
||||
Audience emptyAudience = Audience.empty();
|
||||
|
||||
String string = "Player Name: <filter_name>";
|
||||
|
||||
Component playerExpected = Component.text("Player Name: TestPlayer04");
|
||||
Component emptyExpected = Component.text("Player Name: <filter_name>");
|
||||
|
||||
assertContentEquals(playerExpected, MiniMessage.miniMessage().deserialize(string, player, expansion.audiencePlaceholders()));
|
||||
assertContentEquals(emptyExpected, MiniMessage.miniMessage().deserialize(string, emptyAudience, expansion.audiencePlaceholders()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("AudiencePlaceholder Builder creation")
|
||||
void builderAudiencePlaceholderCreation() {
|
||||
final Expansion expansion = Expansion.builder("builder")
|
||||
.audiencePlaceholder(
|
||||
TestAudience.class,
|
||||
builder -> builder.name("test")
|
||||
.resolver((audience, queue, ctx) -> {
|
||||
final String name = audience.name();
|
||||
return Tag.preProcessParsed(name);
|
||||
})
|
||||
.metadata(PlaceholderMetadata.data("Builder Test", "Returns the Audience Name"))
|
||||
.targetFilter(target -> target.name().length() <= 16)
|
||||
).build();
|
||||
final TestAudience audience = new TestAudience("4drian3d");
|
||||
|
||||
Component expected = Component.text("My name is: 4drian3d");
|
||||
Component parsed = miniMessage().deserialize("My name is: <builder_test>", audience, expansion.audiencePlaceholders());
|
||||
|
||||
assertContentEquals(expected, parsed);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.github.miniplaceholders.test.placeholder;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.test.MiniTest;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class GlobalPlaceholderTest implements MiniTest {
|
||||
@Test
|
||||
@DisplayName("Global Placeholder Test")
|
||||
void globalExpansionPlaceholderTest() {
|
||||
Expansion expansion = Expansion.builder("global")
|
||||
.globalPlaceholder("players", (queue, ctx) -> Tag.selfClosingInserting(Component.text(1305)))
|
||||
.globalPlaceholder("servers", (queue, ctx) -> Tag.selfClosingInserting(Component.text(7)))
|
||||
.build();
|
||||
|
||||
final Component expected = Component.text("Online players: 1305 | Servers: 7");
|
||||
final Component actual = MiniMessage.miniMessage().deserialize("Online players: <global_players> | Servers: <global_servers>",
|
||||
expansion.globalPlaceholders());
|
||||
|
||||
assertContentEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package io.github.miniplaceholders.test.placeholder;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.placeholder.PlaceholderMetadata;
|
||||
import io.github.miniplaceholders.api.types.RelationalAudience;
|
||||
import io.github.miniplaceholders.test.MiniTest;
|
||||
import io.github.miniplaceholders.test.instances.TestAudience;
|
||||
import io.github.miniplaceholders.test.instances.TestAudienceHolder;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class RelationalPlaceholderTest implements MiniTest {
|
||||
@Test
|
||||
@DisplayName("Relational Placeholder Test")
|
||||
void relationalExpansionPlaceholdersTest() {
|
||||
Audience p1 = new TestAudience("PlayerOne");
|
||||
Audience p2 = new TestAudience("PlayerTwo");
|
||||
|
||||
Expansion expansion = Expansion.builder("relational")
|
||||
.relationalPlaceholder(TestAudience.class, "enemy",
|
||||
(aud, relational, queue, ctx) ->
|
||||
Tag.selfClosingInserting(this.isEnemy(aud, relational)
|
||||
? Component.text("Enemy", NamedTextColor.RED)
|
||||
: Component.text("Neutral", NamedTextColor.GREEN)
|
||||
))
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
MiniMessage.miniMessage().deserialize("You are <red>Enemy"),
|
||||
MiniMessage.miniMessage().deserialize("You are <relational_rel_enemy>",
|
||||
RelationalAudience.from(p1, p2),
|
||||
expansion.relationalPlaceholders()));
|
||||
}
|
||||
|
||||
private boolean isEnemy(TestAudience a1, TestAudience a2) {
|
||||
return a1.name().equals("PlayerOne") && a2.name().equals("PlayerTwo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Relational Forwarding Audience Parsing Test")
|
||||
void relationalForwardingAudienceTest() {
|
||||
Expansion expansion = Expansion.builder("test")
|
||||
.relationalPlaceholder(TestAudience.class, "testing",
|
||||
(audience, another, queue, ctx) ->
|
||||
Tag.selfClosingInserting(Component.text("Name: " + audience.name() + ", Other Name: " + another.name())))
|
||||
.build();
|
||||
|
||||
TestAudience testAudience = new TestAudience("4drian3d");
|
||||
TestAudience secondAudience = new TestAudience("PepitoAlcachofa");
|
||||
Audience forward = new TestAudienceHolder(testAudience);
|
||||
Audience secondForward = new TestAudienceHolder(secondAudience);
|
||||
|
||||
Component parsed = miniMessage().deserialize("<test_rel_testing>.",
|
||||
RelationalAudience.from(forward, secondForward), expansion.relationalPlaceholders());
|
||||
Component expected = Component.text("Name: 4drian3d, Other Name: PepitoAlcachofa.");
|
||||
|
||||
assertContentEquals(expected, parsed);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Relational Filtered Expansion")
|
||||
void relationalFilteredExpansion() {
|
||||
Expansion expansion = Expansion.builder("filter")
|
||||
.relationalPlaceholder(TestAudience.class, "name",
|
||||
(aud, another, queue, ctx) ->
|
||||
Tag.selfClosingInserting(Component.text(aud.name().replace(another.name(), ""))))
|
||||
.build();
|
||||
|
||||
Audience player = new TestAudience("TestPlayer04");
|
||||
Audience second = new TestAudience("Player");
|
||||
Audience emptyAudience = Audience.empty();
|
||||
|
||||
String string = "Player Name: <filter_rel_name>";
|
||||
|
||||
Component playerExpected = Component.text("Player Name: Test04");
|
||||
Component emptyExpected = Component.text("Player Name: <filter_rel_name>");
|
||||
|
||||
assertContentEquals(playerExpected, MiniMessage.miniMessage().deserialize(string,
|
||||
RelationalAudience.from(player, second), expansion.relationalPlaceholders()));
|
||||
assertContentEquals(emptyExpected, MiniMessage.miniMessage().deserialize(string,
|
||||
RelationalAudience.from(player, emptyAudience), expansion.relationalPlaceholders()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("RelationalPlaceholder Builder creation")
|
||||
void builderRelationalPlaceholderCreation() {
|
||||
final Expansion expansion = Expansion.builder("builder")
|
||||
.relationalPlaceholder(
|
||||
TestAudience.class,
|
||||
builder -> builder.name("test")
|
||||
.resolver((audience, relational, queue, ctx) -> {
|
||||
final String name = audience.name();
|
||||
final String relationalName = relational.name();
|
||||
return Tag.preProcessParsed(name + " and " + relationalName);
|
||||
})
|
||||
.metadata(PlaceholderMetadata.data("Builder Test", "Returns the Audience plus the Relational Audience combined Names"))
|
||||
.targetFilter(target -> target.name().length() <= 16)
|
||||
).build();
|
||||
final TestAudience audience = new TestAudience("4drian3d");
|
||||
final TestAudience relational = new TestAudience("PepitoAlcachofa");
|
||||
final RelationalAudience<@NotNull TestAudience> relationalAudience = RelationalAudience.from(audience, relational);
|
||||
|
||||
Component expected = Component.text("Our names are: 4drian3d and PepitoAlcachofa");
|
||||
Component parsed = miniMessage().deserialize("Our names are: <builder_test>", relationalAudience, expansion.relationalPlaceholders());
|
||||
|
||||
assertContentEquals(expected, parsed);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ plugins {
|
||||
dependencies {
|
||||
implementation(libs.plugin.shadow)
|
||||
implementation(libs.plugin.kotlin)
|
||||
implementation(libs.plugin.dokka)
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -14,6 +13,6 @@ repositories {
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,5 @@ dependencyResolutionManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.configurationcache.extensions.capitalized
|
||||
|
||||
val Project.capitalizeName: String
|
||||
get() = this.name.substring(17).capitalized()
|
||||
get() = this.name.substring(17)
|
||||
.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
|
||||
|
||||
fun Project.simpleName(): String = this.name.substring(17)
|
||||
@@ -7,7 +7,7 @@ plugins {
|
||||
tasks {
|
||||
compileJava {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
options.release.set(17)
|
||||
options.release.set(21)
|
||||
}
|
||||
jar {
|
||||
manifest {
|
||||
@@ -22,7 +22,7 @@ tasks {
|
||||
|
||||
java{
|
||||
toolchain{
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
vendor.set(JvmVendorSpec.ADOPTIUM)
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
//vendor.set(JvmVendorSpec.ADOPTIUM)
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
signing
|
||||
id("org.jetbrains.dokka")
|
||||
}
|
||||
/*
|
||||
val dokkaHtmlJar = tasks.register<Jar>("dokkaHtmlJar") {
|
||||
dependsOn(tasks.dokkaHtml)
|
||||
from(tasks.dokkaHtml.flatMap { it.outputDirectory })
|
||||
archiveClassifier.set("javadoc")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
artifact(tasks["dokkaHtmlJar"])
|
||||
repositories {
|
||||
maven {
|
||||
credentials(PasswordCredentials::class)
|
||||
val central = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
val snapshots = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||
|
||||
if (project.version.toString().endsWith("SNAPSHOT")) {
|
||||
name = "SonatypeSnapshots"
|
||||
setUrl(snapshots)
|
||||
} else {
|
||||
name = "OSSRH"
|
||||
setUrl(central)
|
||||
}
|
||||
}
|
||||
}
|
||||
from(components["java"])
|
||||
pom {
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
licenses {
|
||||
license {
|
||||
name.set("Apache License 2.0")
|
||||
url.set("https://opensource.org/license/apache-2-0/")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:https://github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
developerConnection.set("scm:git:ssh://git@github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("4drian3d")
|
||||
name.set("Adrian Gonzales")
|
||||
email.set("adriangonzalesval@gmail.com")
|
||||
}
|
||||
}
|
||||
issueManagement {
|
||||
name.set("GitHub")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/issues")
|
||||
}
|
||||
ciManagement {
|
||||
name.set("GitHub Actions")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/actions")
|
||||
}
|
||||
name.set(project.name)
|
||||
description.set(project.description)
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(configurations.archives.get())
|
||||
sign(publishing.publications["maven"])
|
||||
sign(tasks["dokkaHtmlJar"])
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
}*/
|
||||
@@ -1,71 +0,0 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
signing
|
||||
}
|
||||
/*
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
repositories {
|
||||
maven {
|
||||
credentials(PasswordCredentials::class)
|
||||
val central = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
val snapshots = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||
|
||||
if (project.version.toString().endsWith("SNAPSHOT")) {
|
||||
name = "SonatypeSnapshots"
|
||||
setUrl(snapshots)
|
||||
} else {
|
||||
name = "OSSRH"
|
||||
setUrl(central)
|
||||
}
|
||||
}
|
||||
}
|
||||
from(components["java"])
|
||||
pom {
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
licenses {
|
||||
license {
|
||||
name.set("Apache License 2.0")
|
||||
url.set("https://opensource.org/license/apache-2-0/")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:https://github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
developerConnection.set("scm:git:ssh://git@github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("4drian3d")
|
||||
name.set("Adrian Gonzales")
|
||||
email.set("adriangonzalesval@gmail.com")
|
||||
}
|
||||
}
|
||||
issueManagement {
|
||||
name.set("GitHub")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/issues")
|
||||
}
|
||||
ciManagement {
|
||||
name.set("GitHub Actions")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/actions")
|
||||
}
|
||||
name.set(project.name)
|
||||
description.set(project.description)
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(configurations.archives.get())
|
||||
sign(publishing.publications["maven"])
|
||||
|
||||
}*/
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("miniplaceholders.base")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -9,10 +9,10 @@ tasks {
|
||||
archiveClassifier.set("")
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
relocate("net.william278.desertwell", "io.github.miniplaceholders.libs.desertwell")
|
||||
relocate("cloud.commandframework", "io.github.miniplaceholders.libs.cloud")
|
||||
if (project.simpleName() != "sponge") {
|
||||
relocate("io.leangen.geantyref", "io.github.miniplaceholders.libs.geantyref")
|
||||
}
|
||||
relocate("org.incendo.cloud", "io.github.miniplaceholders.libs.cloud")
|
||||
// if (project.simpleName() != "sponge") {
|
||||
// relocate("io.leangen.geantyref", "io.github.miniplaceholders.libs.geantyref")
|
||||
// }
|
||||
destinationDirectory.set(file("${project.rootDir}/jar"))
|
||||
}
|
||||
build {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.22" apply false
|
||||
kotlin("jvm") version "2.2.20" apply false
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -9,12 +9,11 @@ dependencies {
|
||||
implementation(libs.desertwell) {
|
||||
exclude("org.json")
|
||||
}
|
||||
implementation(libs.cloud.core)
|
||||
implementation(libs.cloud.extras) {
|
||||
isTransitive = false
|
||||
}
|
||||
implementation(libs.unnamedinject)
|
||||
compileOnly(libs.adventure.api)
|
||||
compileOnly(libs.adventure.minimesssage)
|
||||
compileOnly(libs.adventure.minimessage)
|
||||
compileOnly(libs.jspecify)
|
||||
compileOnly(libs.brigadier)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
package io.github.miniplaceholders.common;
|
||||
|
||||
import cloud.commandframework.ArgumentDescription;
|
||||
import cloud.commandframework.Command;
|
||||
import cloud.commandframework.CommandManager;
|
||||
import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.standard.StringArgument;
|
||||
import cloud.commandframework.minecraft.extras.AudienceProvider;
|
||||
import cloud.commandframework.minecraft.extras.MinecraftExceptionHandler;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.builder.AbstractBuilder;
|
||||
import net.kyori.adventure.permission.PermissionChecker;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.kyori.adventure.util.TriState;
|
||||
import net.william278.desertwell.about.AboutMenu;
|
||||
import net.william278.desertwell.util.Version;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static net.kyori.adventure.text.Component.newline;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
|
||||
public final class PlaceholdersCommand<A extends Audience> {
|
||||
private final Supplier<ArrayList<String>> playersSuggestions;
|
||||
private final Function<String, Audience> toAudience;
|
||||
private final BiPredicate<A, String> hasPermission;
|
||||
private final String command;
|
||||
private final CommandManager<A> commandManager;
|
||||
|
||||
private PlaceholdersCommand(
|
||||
final Supplier<ArrayList<String>> playersSuggestions,
|
||||
final Function<String, Audience> toAudience,
|
||||
final BiPredicate<A, String> hasPermission,
|
||||
final String command,
|
||||
final CommandManager<A> commandManager
|
||||
) {
|
||||
this.playersSuggestions = playersSuggestions;
|
||||
this.toAudience = toAudience;
|
||||
this.hasPermission = hasPermission;
|
||||
this.command = command;
|
||||
this.commandManager = commandManager;
|
||||
}
|
||||
|
||||
private static final Component TITLE = miniMessage().deserialize("<gradient:#4d8bff:#a4ff96>MiniPlaceholders</gradient>");
|
||||
private static final Component HELP = text()
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:aqua:white:aqua><st><b> </st> <title> <st><b> </st>", Placeholder.component("title", TITLE)))
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:#9694ff:#5269ff>Commands:</gradient>"))
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:aqua:#94d1ff>/miniplaceholders</gradient> <aqua>help</aqua>"))
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:aqua:#94d1ff>/miniplaceholders</gradient> <aqua>parse</aqua> <#8fadff><player | me></#8fadff> <#99ffb6><player></#99ffb6>"))
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:aqua:white:aqua><st><b> "))
|
||||
.build();
|
||||
|
||||
private static final Component INFO = AboutMenu.builder()
|
||||
.title(TITLE)
|
||||
.description(text("MiniMessage Component-based Placeholders for Paper, Folia, Fabric, Velocity, Sponge and Krypton platforms"))
|
||||
.credits("Author", AboutMenu.Credit.of("4drian3d").url("https://github.com/4drian3d"))
|
||||
.credits("Contributors", AboutMenu.Credit.of("Sliman4"))
|
||||
.buttons(
|
||||
AboutMenu.Link.of("https://github.com/MiniPlaceholders/MiniPlaceholders/wiki/User-Getting-Started").text("Documentation").icon("⛏"),
|
||||
AboutMenu.Link.of("https://discord.gg/5NMMzK5mAn").text("Discord").color(TextColor.color(0x7289da)).icon("⭐"),
|
||||
AboutMenu.Link.of("https://modrinth.com/plugin/miniplaceholders").text("Downloads").color(TextColor.color(0xff496e)).icon("↓")
|
||||
).version(Version.fromString(PluginConstants.VERSION))
|
||||
.build()
|
||||
.toComponent();
|
||||
|
||||
private Command.Builder<A> rootBuilder() {
|
||||
return commandManager.commandBuilder(command);
|
||||
}
|
||||
|
||||
public void register() {
|
||||
new MinecraftExceptionHandler<A>()
|
||||
.withDefaultHandlers()
|
||||
.withDecorator(component -> Component.text().append(TITLE).appendSpace().append(component).build())
|
||||
.apply(commandManager, AudienceProvider.nativeAudience());
|
||||
|
||||
commandManager.command(rootBuilder()
|
||||
.permission(src -> {
|
||||
if (hasPermission != null) {
|
||||
return hasPermission.test(src, "miniplaceholders.command");
|
||||
}
|
||||
return src
|
||||
.get(PermissionChecker.POINTER)
|
||||
.map(checker -> checker.value("miniplaceholders.command"))
|
||||
.orElse(TriState.FALSE) != TriState.FALSE;
|
||||
})
|
||||
.handler(handler -> handler.getSender().sendMessage(INFO)));
|
||||
final CommandArgument<A, String> sourceArgument = StringArgument.<A>builder("source")
|
||||
.single()
|
||||
.withDefaultDescription(ArgumentDescription.of("The source from which the message will be parsed"))
|
||||
.withSuggestionsProvider((ctx, st) -> {
|
||||
final List<String> suggestions = playersSuggestions.get();
|
||||
suggestions.add("me");
|
||||
return suggestions;
|
||||
})
|
||||
.build();
|
||||
final CommandArgument<A, String> stringArgument = StringArgument.<A>builder("message")
|
||||
.greedy()
|
||||
.withDefaultDescription(ArgumentDescription.of("Message to be parsed"))
|
||||
.build();
|
||||
|
||||
commandManager.command(
|
||||
rootBuilder().literal("parse")
|
||||
.argument(sourceArgument)
|
||||
.argument(stringArgument)
|
||||
.permission("miniplaceholders.command.parse")
|
||||
.handler(handler -> {
|
||||
final String source = handler.get(sourceArgument);
|
||||
|
||||
final Audience objective = "me".equals(source)
|
||||
? handler.getSender()
|
||||
: toAudience.apply(source);
|
||||
if (objective == null){
|
||||
handler.getSender().sendMessage(text("You must specify a valid player", RED));
|
||||
return;
|
||||
}
|
||||
final String toParse = handler.get(stringArgument);
|
||||
|
||||
final Component parsed = miniMessage().deserialize(
|
||||
toParse,
|
||||
MiniPlaceholders.getAudienceGlobalPlaceholders(objective)
|
||||
);
|
||||
handler.getSender().sendMessage(parsed);
|
||||
})
|
||||
);
|
||||
commandManager.command(
|
||||
rootBuilder().literal("help")
|
||||
.permission("miniplaceholders.command.help")
|
||||
.handler(handler -> handler.getSender().sendMessage(HELP))
|
||||
);
|
||||
}
|
||||
|
||||
public static <A extends Audience> Builder<A> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
public final static class Builder<A extends Audience> implements AbstractBuilder<PlaceholdersCommand<A>> {
|
||||
private Supplier<ArrayList<String>> playersSuggestions;
|
||||
private Function<String, Audience> toAudience;
|
||||
private BiPredicate<A, String> hasPermission;
|
||||
private String command;
|
||||
private CommandManager<A> commandManager;
|
||||
|
||||
private Builder() {}
|
||||
|
||||
public Builder<A> playerSuggestions(final Supplier<@NotNull ArrayList<@NotNull String>> playersSuggestions) {
|
||||
this.playersSuggestions = playersSuggestions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> toAudience(final @NotNull Function<@NotNull String, @Nullable Audience> argumentToAudience) {
|
||||
this.toAudience = argumentToAudience;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> hasPermissionCheck(final @NotNull BiPredicate<@NotNull A, @NotNull String> hasPermission) {
|
||||
this.hasPermission = hasPermission;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> command(final String command) {
|
||||
this.command = command;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A> manager(CommandManager<A> commandManager) {
|
||||
this.commandManager = commandManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlaceholdersCommand<A> build() {
|
||||
return new PlaceholdersCommand<>(
|
||||
requireNonNull(playersSuggestions),
|
||||
requireNonNull(toAudience),
|
||||
hasPermission,
|
||||
command,
|
||||
commandManager
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,135 @@
|
||||
package io.github.miniplaceholders.common;
|
||||
|
||||
public interface PlaceholdersPlugin {
|
||||
void loadDefaultExpansions();
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import io.github.miniplaceholders.api.provider.ExpansionProvider;
|
||||
import io.github.miniplaceholders.api.provider.LoadRequirement;
|
||||
import io.github.miniplaceholders.api.provider.PlatformData;
|
||||
import io.github.miniplaceholders.api.types.Platform;
|
||||
import io.github.miniplaceholders.common.loader.ExpansionProviderLoader;
|
||||
import io.github.miniplaceholders.common.loader.FailedToLoadExpansion;
|
||||
import io.github.miniplaceholders.common.loader.ProviderLoadResult;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import team.unnamed.inject.Injector;
|
||||
|
||||
void registerPlatformCommand();
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface PlaceholdersPlugin {
|
||||
default void registerPlatformCommand() {
|
||||
}
|
||||
|
||||
default void loadProvidedExpansions(Path providersFolderDirectory) throws Exception {
|
||||
final List<Expansion> loadedExpansions = new ArrayList<>();
|
||||
final PlatformData platformData = new PlatformData(this.platformServerInstance(), this);
|
||||
final Injector injector = Injector.create(binder -> binder.bind(PlatformData.class).toInstance(platformData));
|
||||
for (ExpansionProvider provider : ExpansionProviderLoader.loadProvidersFromFolder(providersFolderDirectory)) {
|
||||
injector.injectMembers(provider);
|
||||
ProviderLoadResult loadResult = tryLoad(provider, provider.loadRequirement());
|
||||
final ExpansionProvider loadedProvider = loadResult.provider();
|
||||
if (loadedProvider != null) {
|
||||
final Expansion loadedExpansion = loadedProvider.provideExpansion();
|
||||
loadedExpansions.add(loadedExpansion);
|
||||
loadedExpansion.register();
|
||||
continue;
|
||||
}
|
||||
final FailedToLoadExpansion failedToLoad = loadResult.failed();
|
||||
logError(Component.text("The expansion "
|
||||
+ provider.getClass() +
|
||||
" could not be loaded because the requirement " + failedToLoad.requirement() + " could not be resolved",
|
||||
NamedTextColor.RED));
|
||||
}
|
||||
|
||||
if (loadedExpansions.isEmpty()) {
|
||||
logInfo(Component.text("Not found expansions", NamedTextColor.GRAY));
|
||||
return;
|
||||
}
|
||||
|
||||
final Component expansionsInfo = loadedExpansions.stream()
|
||||
.map(this::expansionInfo)
|
||||
.collect(Component.toComponent(Component.text(", ", NamedTextColor.WHITE)));
|
||||
logInfo(Component.text("Loaded expansions: ", NamedTextColor.GREEN).append(expansionsInfo));
|
||||
}
|
||||
|
||||
private Component expansionInfo(Expansion expansion) {
|
||||
final TextComponent.Builder builder = Component.text();
|
||||
builder.append(Component.text(expansion.name(), NamedTextColor.AQUA));
|
||||
final String author = expansion.author();
|
||||
final String version = expansion.version();
|
||||
if (author == null && version == null) {
|
||||
return builder.build();
|
||||
}
|
||||
builder.append(Component.text('[', NamedTextColor.DARK_GRAY));
|
||||
if (author != null) {
|
||||
builder.append(
|
||||
Component.text("Author", NamedTextColor.GRAY),
|
||||
Component.text(':', NamedTextColor.WHITE),
|
||||
Component.space(),
|
||||
Component.text(author, TextColor.color(0xd4ff54))
|
||||
);
|
||||
}
|
||||
if (version != null) {
|
||||
if (author != null) {
|
||||
builder.append(Component.text(", ", NamedTextColor.WHITE));
|
||||
}
|
||||
builder.append(
|
||||
Component.text("Version", NamedTextColor.GRAY),
|
||||
Component.text(':', NamedTextColor.WHITE),
|
||||
Component.space(),
|
||||
Component.text(version, TextColor.color(0xd4ff54))
|
||||
);
|
||||
}
|
||||
builder.append(Component.text(']', NamedTextColor.DARK_GRAY));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
default ProviderLoadResult tryLoad(final ExpansionProvider provider, final LoadRequirement loadRequirement) {
|
||||
return switch (loadRequirement) {
|
||||
case LoadRequirement.PlatformRequirement platformRequirement -> {
|
||||
for (final Platform supportedPlatform : platformRequirement.platforms()) {
|
||||
if (supportedPlatform == MiniPlaceholders.platform()) {
|
||||
yield ProviderLoadResult.ofLoaded(provider);
|
||||
}
|
||||
}
|
||||
yield ProviderLoadResult.ofFailed(platformRequirement);
|
||||
}
|
||||
case LoadRequirement.AvailableComplementRequirement complementRequirement ->
|
||||
complementRequirement.shouldLoad(this::platformHasComplementLoaded)
|
||||
? ProviderLoadResult.ofLoaded(provider)
|
||||
: ProviderLoadResult.ofFailed(complementRequirement);
|
||||
case LoadRequirement.MultiLoadRequirement multiLoadRequirement -> {
|
||||
ProviderLoadResult loadResult;
|
||||
for (final LoadRequirement requirement : multiLoadRequirement.requirements()) {
|
||||
if ((loadResult = this.tryLoad(provider, requirement)).failed() != null) {
|
||||
yield loadResult;
|
||||
}
|
||||
}
|
||||
yield ProviderLoadResult.ofLoaded(provider);
|
||||
}
|
||||
case LoadRequirement.ClassRequirement classRequirement -> classRequirement.shouldLoad()
|
||||
? ProviderLoadResult.ofLoaded(provider) : ProviderLoadResult.ofFailed(classRequirement);
|
||||
case LoadRequirement.AnyOfRequirement anyOfRequirement -> {
|
||||
ProviderLoadResult loadResult;
|
||||
for (final LoadRequirement requirement : anyOfRequirement.requirements()) {
|
||||
if ((loadResult = this.tryLoad(provider, requirement)).provider() != null) {
|
||||
yield loadResult;
|
||||
}
|
||||
}
|
||||
yield ProviderLoadResult.ofFailed(anyOfRequirement);
|
||||
}
|
||||
case LoadRequirement.NoneLoadRequirement ignored -> ProviderLoadResult.ofLoaded(provider);
|
||||
};
|
||||
}
|
||||
|
||||
boolean platformHasComplementLoaded(String complementName);
|
||||
|
||||
void logError(Component component);
|
||||
|
||||
void logInfo(Component component);
|
||||
|
||||
Object platformServerInstance();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.github.miniplaceholders.common.command;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface AudienceConverter {
|
||||
@Nullable Audience convert(@NotNull String string);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.github.miniplaceholders.common.command;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
@FunctionalInterface
|
||||
public interface AudienceExtractor<S> {
|
||||
Audience extract(S source);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package io.github.miniplaceholders.common.command;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import io.github.miniplaceholders.api.types.Platform;
|
||||
import io.github.miniplaceholders.common.PluginConstants;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.william278.desertwell.about.AboutMenu;
|
||||
import net.william278.desertwell.util.Version;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.kyori.adventure.text.Component.newline;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
|
||||
public final class CommandConstants {
|
||||
// Global
|
||||
public static final Component TITLE = miniMessage().deserialize("<b><gradient:#4d8bff:#a4ff96>MiniPlaceholders</b>");
|
||||
public static final Component HEADER = miniMessage().deserialize(
|
||||
"""
|
||||
<gradient:aqua:white:aqua><st><b> </st> \
|
||||
<title> \
|
||||
<st><b> </st>""", Placeholder.component("title", TITLE));
|
||||
public static final Component FOOTER = miniMessage().deserialize(
|
||||
"<gradient:aqua:white:aqua><st><b> ");
|
||||
|
||||
// -- /miniplaceholders --
|
||||
public static final Component INFO = AboutMenu.builder()
|
||||
.title(TITLE)
|
||||
.description(text("MiniMessage Component-based Placeholders API"))
|
||||
.credits("Author", AboutMenu.Credit.of("4drian3d").url("https://github.com/4drian3d"))
|
||||
.credits("Contributors",
|
||||
AboutMenu.Credit.of("Sliman4"),
|
||||
AboutMenu.Credit.of("Stampede2011"),
|
||||
AboutMenu.Credit.of("powercasgamer"),
|
||||
AboutMenu.Credit.of("fabianmakila"),
|
||||
AboutMenu.Credit.of("Willsr71")
|
||||
)
|
||||
.buttons(
|
||||
AboutMenu.Link.of("https://miniplaceholders.github.io/docs/category/user-guide").text("Documentation").icon("⛏"),
|
||||
AboutMenu.Link.of("https://discord.gg/5NMMzK5mAn").text("Discord").color(TextColor.color(0x7289da)).icon("⭐"),
|
||||
AboutMenu.Link.of("https://modrinth.com/plugin/miniplaceholders").text("Downloads").color(TextColor.color(0xff496e)).icon("↓")
|
||||
).version(Version.fromString(PluginConstants.VERSION))
|
||||
.build()
|
||||
.toComponent();
|
||||
|
||||
// -- /miniplaceholders help --
|
||||
public static final Supplier<Component> HELP = Suppliers.memoize(() -> {
|
||||
final Platform platform = MiniPlaceholders.platform();
|
||||
final String command = platform == Platform.VELOCITY ? "/vminiplaceholders" : "/miniplaceholders";
|
||||
return text()
|
||||
.append(newline(), HEADER)
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:#9694ff:#5269ff>Commands:</gradient>")
|
||||
)
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:aqua:#94d1ff>" + command + "</gradient> <aqua>help</aqua>")
|
||||
.clickEvent(ClickEvent.runCommand(command + " help"))
|
||||
)
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"""
|
||||
<gradient:aqua:#94d1ff>%s</gradient> \
|
||||
<aqua>parse</aqua> \
|
||||
<#8fadff><player | me | --null></#8fadff> \
|
||||
<#99ffb6><message></#99ffb6>""".formatted(command))
|
||||
.clickEvent(ClickEvent.suggestCommand(command + " parse "))
|
||||
)
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"""
|
||||
<gradient:aqua:#94d1ff>%s</gradient> \
|
||||
<aqua>parserel</aqua> \
|
||||
<#8fadff><player | me></#8fadff> \
|
||||
<#8fadff><relational player | me></#8fadff> \
|
||||
<#99ffb6><message></#99ffb6>""".formatted(command))
|
||||
.clickEvent(ClickEvent.suggestCommand(command + " parserel "))
|
||||
)
|
||||
.append(newline(), miniMessage().deserialize(
|
||||
"<gradient:aqua:#94d1ff>" + command + "</gradient> <aqua>expansions</aqua>")
|
||||
.clickEvent(ClickEvent.runCommand(command + " help"))
|
||||
)
|
||||
.append(newline(), FOOTER)
|
||||
.build();
|
||||
});
|
||||
|
||||
// -- /miniplaceholders expansions --
|
||||
public static final Component EXPANSIONS_TITLE = miniMessage().deserialize(
|
||||
"""
|
||||
<br><gradient:aqua:white:aqua><st><b> </st> \
|
||||
<title> Expansions <st><b> \
|
||||
</gradient><br>""", Placeholder.component("title", TITLE)
|
||||
);
|
||||
|
||||
public static final Supplier<Component> NON_INSTALLED_EXPANSIONS = Suppliers.memoize(() -> miniMessage().deserialize(
|
||||
"""
|
||||
<aqua>It seems that you don't have any <#a1ff4a>Expansion</#a1ff4a> installed yet,
|
||||
You can find the <#a1ff4a>Expansion</#a1ff4a> you want here <br>\
|
||||
<blue><click:open_url:"https://miniplaceholders.github.io/docs/user-guide/Placeholders">\
|
||||
<hover:show_text:"<gold>Search Expansions">[MiniPlaceholders Expansions]</blue>""")
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package io.github.miniplaceholders.common.command;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.util.TriState;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
@FunctionalInterface
|
||||
public interface PermissionTester {
|
||||
TriState permissionValue(Audience audience, String permission);
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package io.github.miniplaceholders.common.command;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlaceholderCommandExecutor {
|
||||
void execute(@NonNull Audience audience);
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package io.github.miniplaceholders.common.command;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayersNameProvider {
|
||||
@NonNull ArrayList<String> provide();
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package io.github.miniplaceholders.common.command.brigadier;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import io.github.miniplaceholders.common.command.*;
|
||||
import io.github.miniplaceholders.common.command.node.*;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class BrigadierCommandProvider {
|
||||
public static <A> LiteralCommandNode<A> provideCommand(
|
||||
final String command,
|
||||
final PlayersNameProvider playersSuggestions,
|
||||
final AudienceConverter audienceConverter,
|
||||
final AudienceExtractor<A> audienceExtractor,
|
||||
final PermissionTester permissionChecker
|
||||
) {
|
||||
// Inspired by MiniMotd command implementation (MIT licenced)
|
||||
class WrappingExecutor implements Command<A> {
|
||||
private final PlaceholderCommandExecutor executor;
|
||||
|
||||
private WrappingExecutor(final PlaceholderCommandExecutor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int run(final CommandContext<A> commandContext) {
|
||||
executor.execute(audienceExtractor.extract(commandContext.getSource()));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// Command Nodes
|
||||
final RootNode rootNode = new RootNode(permissionChecker);
|
||||
final ExpansionsNode expansionsNode = new ExpansionsNode(permissionChecker);
|
||||
final HelpNode helpNode = new HelpNode(permissionChecker);
|
||||
final ParseNode parseNode = new ParseNode(playersSuggestions, audienceConverter, permissionChecker);
|
||||
final ParseRelNode parseRelNode = new ParseRelNode(playersSuggestions, audienceConverter, permissionChecker);
|
||||
|
||||
return LiteralArgumentBuilder.<A>literal(command)
|
||||
.requires(src -> rootNode.hasPermission(audienceExtractor.extract(src)))
|
||||
.executes(new WrappingExecutor(rootNode::execute))
|
||||
.then(LiteralArgumentBuilder.<A>literal("expansions")
|
||||
.requires(src -> expansionsNode.hasPermission(audienceExtractor.extract(src)))
|
||||
.executes(new WrappingExecutor(expansionsNode::showExpansions))
|
||||
.build()
|
||||
)
|
||||
.then(LiteralArgumentBuilder.<A>literal("help")
|
||||
.executes(new WrappingExecutor(helpNode::execute))
|
||||
.requires(src -> helpNode.hasPermission(audienceExtractor.extract(src)))
|
||||
)
|
||||
.then(LiteralArgumentBuilder.<A>literal("parse")
|
||||
.requires(src -> parseNode.hasPermission(audienceExtractor.extract(src)))
|
||||
.then(RequiredArgumentBuilder.<A, String>argument("source", StringArgumentType.word())
|
||||
.suggests(new BrigadierSuggestionProvider<>(parseNode))
|
||||
.then(RequiredArgumentBuilder.<A, String>argument("message", StringArgumentType.greedyString())
|
||||
.executes(ctx -> {
|
||||
final Audience audience = audienceExtractor.extract(ctx.getSource());
|
||||
final String source = StringArgumentType.getString(ctx, "source");
|
||||
final String message = StringArgumentType.getString(ctx, "message");
|
||||
|
||||
parseNode.parseString(audience, source, message);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(LiteralArgumentBuilder.<A>literal("parserel")
|
||||
.requires(src -> parseRelNode.hasPermission(audienceExtractor.extract(src)))
|
||||
.then(RequiredArgumentBuilder.<A, String>argument("source", StringArgumentType.word())
|
||||
.suggests(new BrigadierSuggestionProvider<>(parseRelNode))
|
||||
.then(RequiredArgumentBuilder.<A, String>argument("relational", StringArgumentType.word())
|
||||
.suggests(new BrigadierSuggestionProvider<>(parseRelNode))
|
||||
.then(RequiredArgumentBuilder.<A, String>argument("message", StringArgumentType.greedyString())
|
||||
.executes(ctx -> {
|
||||
final Audience audience = audienceExtractor.extract(ctx.getSource());
|
||||
final String source = StringArgumentType.getString(ctx, "source");
|
||||
final String relational = StringArgumentType.getString(ctx, "source");
|
||||
final String message = StringArgumentType.getString(ctx, "message");
|
||||
|
||||
parseRelNode.parseString(audience, source, relational, message);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.github.miniplaceholders.common.command.brigadier;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import io.github.miniplaceholders.common.command.node.PlayerCompleterNode;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@NullMarked
|
||||
public record BrigadierSuggestionProvider<A>(PlayerCompleterNode playerSuggestions) implements SuggestionProvider<A> {
|
||||
@Override
|
||||
public CompletableFuture<Suggestions> getSuggestions(final CommandContext<A> context, final SuggestionsBuilder builder) {
|
||||
this.playerSuggestions.providePlayerSuggestions().forEach(builder::suggest);
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package io.github.miniplaceholders.common.command.node;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import io.github.miniplaceholders.common.command.PermissionTester;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static io.github.miniplaceholders.common.command.CommandConstants.*;
|
||||
|
||||
@NullMarked
|
||||
public record ExpansionsNode(PermissionTester permissionTester) implements Node {
|
||||
public void showExpansions(final Audience audience) {
|
||||
final TextComponent.Builder builder = Component.text()
|
||||
.append(EXPANSIONS_TITLE);
|
||||
final Collection<Expansion> expansions = MiniPlaceholders.expansionsAvailable();
|
||||
if (expansions.isEmpty()) {
|
||||
// In virtually all installations, there will always be some expansion installed,
|
||||
// so there is no need to cache this component in that case
|
||||
builder.append(NON_INSTALLED_EXPANSIONS.get());
|
||||
} else {
|
||||
builder.append(expansions.stream()
|
||||
.map(expansion -> {
|
||||
final TextComponent.Builder expansionMetaBuilder = Component.text();
|
||||
final String expansionName = expansion.name();
|
||||
final String expansionAuthor = expansion.author();
|
||||
final String expansionVersion = expansion.version();
|
||||
|
||||
expansionMetaBuilder.append(Component.text(expansionName, NamedTextColor.GRAY));
|
||||
if (expansionAuthor != null) {
|
||||
expansionMetaBuilder.appendNewline()
|
||||
.append(Component.text("Author: ", TextColor.color(0x29fbff)))
|
||||
.append(Component.text(expansionAuthor, NamedTextColor.GRAY));
|
||||
}
|
||||
if (expansionVersion != null) {
|
||||
expansionMetaBuilder.appendNewline()
|
||||
.append(Component.text("Version: ", TextColor.color(0xff7b61)))
|
||||
.append(Component.text(expansionVersion, NamedTextColor.GRAY));
|
||||
}
|
||||
return Component.text()
|
||||
.content(expansion.name())
|
||||
.hoverEvent(expansionMetaBuilder.build())
|
||||
.build();
|
||||
}).collect(Component.toComponent(Component.text(" | ", NamedTextColor.DARK_GRAY))));
|
||||
}
|
||||
builder.appendNewline().append(FOOTER);
|
||||
audience.sendMessage(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Audience audience) {
|
||||
return permissionTester.permissionValue(audience, permission()).toBooleanOrElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "miniplaceholders.command.expansions";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.github.miniplaceholders.common.command.node;
|
||||
|
||||
import io.github.miniplaceholders.common.command.PermissionTester;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static io.github.miniplaceholders.common.command.CommandConstants.HELP;
|
||||
|
||||
@NullMarked
|
||||
public record HelpNode(PermissionTester permissionChecker) implements Node {
|
||||
public void execute(final Audience audience) {
|
||||
audience.sendMessage(HELP.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Audience audience) {
|
||||
return permissionChecker.permissionValue(audience, permission()).toBooleanOrElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "miniplaceholders.command.help";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package io.github.miniplaceholders.common.command.node;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public sealed interface Node permits ExpansionsNode, HelpNode, ParseNode, ParseRelNode, RootNode {
|
||||
boolean hasPermission(Audience audience);
|
||||
|
||||
String permission();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package io.github.miniplaceholders.common.command.node;
|
||||
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import io.github.miniplaceholders.common.command.AudienceConverter;
|
||||
import io.github.miniplaceholders.common.command.PermissionTester;
|
||||
import io.github.miniplaceholders.common.command.PlayersNameProvider;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
|
||||
@NullMarked
|
||||
public record ParseNode(
|
||||
PlayersNameProvider playersNameProvider,
|
||||
AudienceConverter audienceConverter,
|
||||
PermissionTester permissionChecker
|
||||
) implements Node, PlayerCompleterNode {
|
||||
public void parseString(final Audience sender, final String providedSource, final String providedString) {
|
||||
final Audience source = switch (providedSource) {
|
||||
case "me" -> sender;
|
||||
case "--null" -> Audience.empty();
|
||||
default -> audienceConverter.convert(providedSource);
|
||||
};
|
||||
if (source == null) {
|
||||
sender.sendMessage(text("You must specify a valid player", RED));
|
||||
return;
|
||||
}
|
||||
|
||||
final TagResolver resolver = source == Audience.empty()
|
||||
? MiniPlaceholders.globalPlaceholders()
|
||||
: MiniPlaceholders.audienceGlobalPlaceholders();
|
||||
|
||||
final Component parsed = miniMessage().deserialize(providedString, source, resolver);
|
||||
sender.sendMessage(parsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final Audience audience) {
|
||||
return permissionChecker.permissionValue(audience, permission()).toBooleanOrElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "miniplaceholders.command.parse";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> providePlayerSuggestions() {
|
||||
final List<String> suggestions = PlayerCompleterNode.super.providePlayerSuggestions();
|
||||
suggestions.add("--null");
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package io.github.miniplaceholders.common.command.node;
|
||||
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import io.github.miniplaceholders.api.types.RelationalAudience;
|
||||
import io.github.miniplaceholders.common.command.AudienceConverter;
|
||||
import io.github.miniplaceholders.common.command.PermissionTester;
|
||||
import io.github.miniplaceholders.common.command.PlayersNameProvider;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage;
|
||||
|
||||
@NullMarked
|
||||
public record ParseRelNode(
|
||||
PlayersNameProvider playersNameProvider,
|
||||
AudienceConverter audienceConverter,
|
||||
PermissionTester permissionChecker
|
||||
) implements Node, PlayerCompleterNode {
|
||||
|
||||
public void parseString(final Audience sender, final String source, final String relational, final String toParse) {
|
||||
final Audience senderTarget = "me".equals(source)
|
||||
? sender
|
||||
: audienceConverter.convert(source);
|
||||
if (senderTarget == null) {
|
||||
sender.sendMessage(text("You must specify a valid player", RED));
|
||||
return;
|
||||
}
|
||||
|
||||
final Audience relationalTarget = "me".equals(relational)
|
||||
? sender
|
||||
: audienceConverter.convert(relational);
|
||||
if (relationalTarget == null) {
|
||||
sender.sendMessage(text("You must specify a valid relational player", RED));
|
||||
return;
|
||||
}
|
||||
|
||||
final RelationalAudience<Audience> relationalAudience = RelationalAudience.from(senderTarget, relationalTarget);
|
||||
|
||||
final Component parsed = miniMessage().deserialize(
|
||||
toParse,
|
||||
relationalAudience,
|
||||
MiniPlaceholders.relationalGlobalPlaceholders()
|
||||
);
|
||||
sender.sendMessage(parsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Audience audience) {
|
||||
return permissionChecker.permissionValue(audience, permission()).toBooleanOrElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "miniplaceholders.command.parse";
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package io.github.miniplaceholders.common.command.node;
|
||||
|
||||
import io.github.miniplaceholders.common.command.PlayersNameProvider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PlayerCompleterNode {
|
||||
default List<String> providePlayerSuggestions() {
|
||||
final List<String> suggestions = playersNameProvider().provide();
|
||||
suggestions.add("me");
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
PlayersNameProvider playersNameProvider();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.github.miniplaceholders.common.command.node;
|
||||
|
||||
import io.github.miniplaceholders.common.command.PermissionTester;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.util.TriState;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static io.github.miniplaceholders.common.command.CommandConstants.INFO;
|
||||
|
||||
@NullMarked
|
||||
public record RootNode(PermissionTester permissionChecker) implements Node {
|
||||
|
||||
public void execute(Audience audience) {
|
||||
audience.sendMessage(INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Audience audience) {
|
||||
return permissionChecker.permissionValue(audience, permission()) != TriState.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "miniplaceholders.command";
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package io.github.miniplaceholders.common.loader;
|
||||
|
||||
import io.github.miniplaceholders.api.provider.ExpansionProvider;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
@NullMarked
|
||||
public final class ExpansionProviderLoader {
|
||||
|
||||
public static List<ExpansionProvider> loadProvidersFromFolder(final Path folferPath) throws Exception {
|
||||
if (Files.notExists(folferPath)) {
|
||||
try {
|
||||
Files.createDirectories(folferPath);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to create expansions directory", e);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
try (final DirectoryStream<Path> stream = Files.newDirectoryStream(folferPath,
|
||||
filtered -> Files.isRegularFile(filtered) && filtered.toString().endsWith(".jar"))) {
|
||||
final List<ExpansionProvider> providerList = new ArrayList<>();
|
||||
for (final Path possibleProviderPath : stream) {
|
||||
loadPossibleProviderFromPath(possibleProviderPath, providerList);
|
||||
}
|
||||
return providerList;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadPossibleProviderFromPath(
|
||||
final Path filePath,
|
||||
final List<ExpansionProvider> providers
|
||||
) throws Exception {
|
||||
String mainClassName = null;
|
||||
try (final JarInputStream jarStream = new JarInputStream(Files.newInputStream(filePath))) {
|
||||
JarEntry actualEntry;
|
||||
while ((actualEntry = jarStream.getNextJarEntry()) != null) {
|
||||
final String entryName = actualEntry.getName();
|
||||
if (!Objects.equals(entryName, "expansion-provider.properties")) {
|
||||
continue;
|
||||
}
|
||||
try (final Reader reader = new InputStreamReader(jarStream)) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(reader);
|
||||
mainClassName = properties.getProperty("provider-class");
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
throw new IOException(
|
||||
"Unable to read expansion_provider.properties from " + filePath + "file",
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mainClassName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadFoundProvider(mainClassName, providers, filePath);
|
||||
}
|
||||
|
||||
private static void loadFoundProvider(
|
||||
final String providerClass,
|
||||
final List<ExpansionProvider> providers,
|
||||
final Path providerPath
|
||||
) throws Exception {
|
||||
//noinspection resource
|
||||
final URLClassLoader classLoader = new URLClassLoader(new URL[] {
|
||||
providerPath.toUri().toURL(),
|
||||
}, ExpansionProviderLoader.class.getClassLoader());
|
||||
Class<?> mainClass;
|
||||
try {
|
||||
mainClass = classLoader.loadClass(providerClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ClassNotFoundException("Unable to load main class from file " + providerPath, e);
|
||||
}
|
||||
if (!ExpansionProvider.class.isAssignableFrom(mainClass) || mainClass.isInterface()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
providers.add((ExpansionProvider) mainClass.getConstructors()[0].newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to create expansion provider from file " + providerPath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.github.miniplaceholders.common.loader;
|
||||
|
||||
import io.github.miniplaceholders.api.provider.LoadRequirement;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public record FailedToLoadExpansion(@NonNull LoadRequirement requirement) {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.github.miniplaceholders.common.loader;
|
||||
|
||||
import io.github.miniplaceholders.api.provider.ExpansionProvider;
|
||||
import io.github.miniplaceholders.api.provider.LoadRequirement;
|
||||
|
||||
public record ProviderLoadResult(ExpansionProvider provider, FailedToLoadExpansion failed) {
|
||||
public static ProviderLoadResult ofLoaded(ExpansionProvider provider) {
|
||||
return new ProviderLoadResult(provider, null);
|
||||
}
|
||||
|
||||
public static ProviderLoadResult ofFailed(LoadRequirement requirement) {
|
||||
return new ProviderLoadResult(null, new FailedToLoadExpansion(requirement));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,8 @@ package io.github.miniplaceholders.connect;
|
||||
|
||||
public enum InternalPlatform {
|
||||
|
||||
PAPER, VELOCITY, KRYPTON, FABRIC, SPONGE;
|
||||
PAPER, VELOCITY,
|
||||
FABRIC, SPONGE;
|
||||
|
||||
private static InternalPlatform actualPlatform;
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
id("miniplaceholders.auto.module")
|
||||
alias(libs.plugins.idea.ext)
|
||||
alias(libs.plugins.blossom)
|
||||
}
|
||||
|
||||
version = "1.0.0"
|
||||
|
||||
dependencies {
|
||||
compileOnly(projects.miniplaceholdersApi)
|
||||
compileOnly(libs.adventure.api)
|
||||
compileOnly(libs.adventure.minimessage)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
blossom {
|
||||
javaSources {
|
||||
property("version", project.version.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.github.miniplaceholders.provider.example;
|
||||
|
||||
public final class ExpansionConstants {
|
||||
public static final String VERSION = "{{ version }}";
|
||||
|
||||
private ExpansionConstants() {}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package io.miniplaceholders.provider.example;
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.api.provider.ExpansionProvider;
|
||||
import io.github.miniplaceholders.api.provider.LoadRequirement;
|
||||
import io.github.miniplaceholders.api.types.Platform;
|
||||
import io.github.miniplaceholders.provider.example.ExpansionConstants;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
|
||||
public final class ExampleProvider implements ExpansionProvider {
|
||||
// For advanced usage only
|
||||
@team.unnamed.inject.Inject
|
||||
private io.github.miniplaceholders.api.provider.PlatformData platformData;
|
||||
|
||||
@Override
|
||||
public Expansion provideExpansion() {
|
||||
|
||||
return Expansion.builder("example")
|
||||
.version("2.0.0")
|
||||
.author("MiniPlaceholders Contributors")
|
||||
.globalPlaceholder("test", (queue, context) ->
|
||||
Tag.preProcessParsed("""
|
||||
Platform Instance Class: %s
|
||||
MiniPlaceholders Instance Class: %s
|
||||
""".formatted(
|
||||
platformData.serverInstance(),
|
||||
platformData.complementInstance())))
|
||||
.version(ExpansionConstants.VERSION)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadRequirement loadRequirement() {
|
||||
return LoadRequirement.allOf(LoadRequirement.allOf(LoadRequirement.platform(Platform.VELOCITY)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
provider-class=io.miniplaceholders.provider.example.ExampleProvider
|
||||
+10
-7
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("miniplaceholders.auto.module")
|
||||
id("fabric-loom")
|
||||
id("quiet-fabric-loom")
|
||||
alias(libs.plugins.shadow)
|
||||
}
|
||||
|
||||
@@ -14,11 +14,10 @@ dependencies {
|
||||
|
||||
includeDependency(libs.adventure.platform.fabric)
|
||||
includeDependency(libs.adventure.serializer.legacy)
|
||||
includeDependency(libs.adventure.minimessage)
|
||||
includeDependency(libs.luckopermissionsapi)
|
||||
includeDependency(libs.cloud.fabric)
|
||||
includeDependency(libs.cloud.core)
|
||||
includeDependency(libs.cloud.extras)
|
||||
includeDependency(libs.desertwell)
|
||||
includeDependency(libs.unnamedinject)
|
||||
|
||||
shadeModule(projects.miniplaceholdersApi)
|
||||
shadeModule(projects.miniplaceholdersCommon)
|
||||
@@ -39,17 +38,21 @@ fun DependencyHandlerScope.includeDependency(dependency: Any) {
|
||||
include(dependency)
|
||||
}
|
||||
|
||||
|
||||
|
||||
tasks {
|
||||
val projectVersion = project.version
|
||||
val rootDir = project.rootDir
|
||||
processResources {
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand("version" to project.version)
|
||||
expand("version" to projectVersion)
|
||||
}
|
||||
}
|
||||
remapJar {
|
||||
inputFile.set(shadowJar.get().archiveFile)
|
||||
archiveFileName.set("MiniPlaceholders-Fabric-${project.version}.jar")
|
||||
destinationDirectory.set(file("${project.rootDir}/jar"))
|
||||
archiveFileName.set("MiniPlaceholders-Fabric-${projectVersion}.jar")
|
||||
destinationDirectory.set(file("${rootDir}/jar"))
|
||||
}
|
||||
shadowJar {
|
||||
configurations = listOf(shade)
|
||||
|
||||
@@ -1,113 +1,86 @@
|
||||
package io.github.miniplaceholders.fabric;
|
||||
|
||||
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
|
||||
import cloud.commandframework.fabric.FabricServerCommandManager;
|
||||
import io.github.miniplaceholders.api.Expansion;
|
||||
import io.github.miniplaceholders.common.PlaceholdersCommand;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import io.github.miniplaceholders.common.PlaceholdersPlugin;
|
||||
import io.github.miniplaceholders.common.command.brigadier.BrigadierCommandProvider;
|
||||
import io.github.miniplaceholders.connect.InternalPlatform;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.api.DedicatedServerModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
import net.kyori.adventure.util.TriState;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.packs.PackResources;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class FabricMod implements ModInitializer, PlaceholdersPlugin {
|
||||
private final Logger logger = LoggerFactory.getLogger("miniplaceholders");
|
||||
private static final DecimalFormat MSPT_FORMAT = new DecimalFormat("###.###");
|
||||
public final class FabricMod implements DedicatedServerModInitializer, PlaceholdersPlugin {
|
||||
private final ComponentLogger componentLogger = ComponentLogger.logger("miniplaceholders");
|
||||
private MinecraftServer minecraftServer;
|
||||
|
||||
private final TickManager tickManager = new TickManager();
|
||||
private MinecraftServer minecraftServer;
|
||||
@Override
|
||||
public void onInitializeServer() {
|
||||
componentLogger.info("Starting MiniPlaceholders Fabric");
|
||||
InternalPlatform.platform(InternalPlatform.FABRIC);
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
logger.info("Starting MiniPlaceholders Fabric");
|
||||
InternalPlatform.platform(InternalPlatform.FABRIC);
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer -> {
|
||||
this.minecraftServer = minecraftServer;
|
||||
try {
|
||||
final Path expansionsFolder = FabricLoader.getInstance().getConfigDir()
|
||||
.resolve("miniplaceholders").resolve("expansions");
|
||||
this.loadProvidedExpansions(expansionsFolder);
|
||||
} catch (Throwable e) {
|
||||
componentLogger.error("Unable to load expansion providers", e);
|
||||
}
|
||||
});
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer -> {
|
||||
this.minecraftServer = minecraftServer;
|
||||
this.loadDefaultExpansions();
|
||||
});
|
||||
this.registerPlatformCommand();
|
||||
}
|
||||
|
||||
this.registerPlatformCommand();
|
||||
}
|
||||
@Override
|
||||
public void registerPlatformCommand() {
|
||||
final LiteralCommandNode<CommandSourceStack> node = BrigadierCommandProvider.provideCommand(
|
||||
"miniplaceholders",
|
||||
() -> new ArrayList<>(List.of(this.minecraftServer.getPlayerNames())),
|
||||
string -> this.minecraftServer.getPlayerList().getPlayerByName(string),
|
||||
aud -> aud,
|
||||
(audience, permission) -> {
|
||||
final CommandSourceStack commandSourceStack = (CommandSourceStack) audience;
|
||||
return switch (Permissions.getPermissionValue(commandSourceStack, permission)) {
|
||||
case TRUE -> TriState.TRUE;
|
||||
case FALSE -> TriState.FALSE;
|
||||
case DEFAULT -> commandSourceStack.hasPermission(3) ? TriState.TRUE : TriState.NOT_SET;
|
||||
};
|
||||
}
|
||||
);
|
||||
CommandRegistrationCallback.EVENT.register(
|
||||
(dispatcher, ctx, selection) ->
|
||||
dispatcher.getRoot().addChild(node)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDefaultExpansions() {
|
||||
Expansion.builder("server")
|
||||
.globalPlaceholder("name", Tag.preProcessParsed(this.minecraftServer.getServerModName()))
|
||||
.globalPlaceholder("online", (queue, ctx) -> Tag.preProcessParsed(Integer.toString(this.minecraftServer.getPlayerCount())))
|
||||
.globalPlaceholder("version", (ctx, queue) -> Tag.preProcessParsed(this.minecraftServer.getServerVersion()))
|
||||
.globalPlaceholder("max_players", (ctx, queue) -> Tag.preProcessParsed(Integer.toString(this.minecraftServer.getMaxPlayers())))
|
||||
.globalPlaceholder("unique_joins", (queue, ctx) -> Tag.preProcessParsed(Integer.toString(this.minecraftServer.getProfileCache().load().size())))
|
||||
.globalPlaceholder("tps_1m", (ctx, queue) -> Tag.preProcessParsed(tickManager.getTps1m().formattedAverage()))
|
||||
.globalPlaceholder("tps_5m", (ctx, queue) -> Tag.preProcessParsed(tickManager.getTps5m().formattedAverage()))
|
||||
//.globalPlaceholder("tps_15m", (ctx, queue) -> Tag.preProcessParsed(tickManager.getTps15m().formattedAverage()))
|
||||
.globalPlaceholder("tick_count", (queue, ctx) -> Tag.preProcessParsed(Integer.toString(this.minecraftServer.getTickCount())))
|
||||
.globalPlaceholder("has_whitelist", (queue, ctx) -> Tag.preProcessParsed(Boolean.toString(this.minecraftServer.getPlayerList().isUsingWhitelist())))
|
||||
.globalPlaceholder("total_chunks", (queue, ctx) -> {
|
||||
int chunks = 0;
|
||||
for (final ServerLevel level : this.minecraftServer.getAllLevels()) {
|
||||
chunks += level.getChunkSource().getLoadedChunksCount();
|
||||
}
|
||||
return Tag.preProcessParsed(Integer.toString(chunks));
|
||||
})
|
||||
.globalPlaceholder("total_entities", (queue, ctx) -> {
|
||||
int entities = 0;
|
||||
for (final ServerLevel level : this.minecraftServer.getAllLevels()) {
|
||||
for (final Entity ignored : level.getAllEntities()) {
|
||||
entities++;
|
||||
}
|
||||
}
|
||||
return Tag.preProcessParsed(Integer.toString(entities));
|
||||
})
|
||||
.globalPlaceholder("mspt", (queue, ctx) -> Tag.preProcessParsed(MSPT_FORMAT.format(this.minecraftServer.getAverageTickTime())))
|
||||
.globalPlaceholder("datapack_list", (queue, ctx) ->
|
||||
Tag.selfClosingInserting(this.minecraftServer.getResourceManager()
|
||||
.listPacks()
|
||||
.map(PackResources::packId)
|
||||
.map(id -> Component.text()
|
||||
.content("[")
|
||||
.append(Component.text(id))
|
||||
.append(Component.text("]"))
|
||||
.build())
|
||||
.collect(Component.toComponent(Component.space())))
|
||||
)
|
||||
.globalPlaceholder("datapack_count", (queue, ctx) ->
|
||||
Tag.preProcessParsed(Long.toString(this.minecraftServer.getResourceManager()
|
||||
.listPacks()
|
||||
.count()))
|
||||
)
|
||||
.build()
|
||||
.register();
|
||||
}
|
||||
@Override
|
||||
public boolean platformHasComplementLoaded(String complementName) {
|
||||
return FabricLoader.getInstance().isModLoaded(complementName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerPlatformCommand() {
|
||||
final FabricServerCommandManager<CommandSourceStack> commandManager = new FabricServerCommandManager<>(
|
||||
AsynchronousCommandExecutionCoordinator.simpleCoordinator(),
|
||||
Function.identity(),
|
||||
Function.identity()
|
||||
);
|
||||
PlaceholdersCommand.<CommandSourceStack>builder()
|
||||
.hasPermissionCheck((source, permission) -> Permissions.check(source, permission, 4))
|
||||
.toAudience(string -> this.minecraftServer.getPlayerList().getPlayerByName(string))
|
||||
.playerSuggestions(() -> new ArrayList<>(List.of(this.minecraftServer.getPlayerNames())))
|
||||
.manager(commandManager)
|
||||
.command("miniplaceholders")
|
||||
.build()
|
||||
.register();
|
||||
}
|
||||
@Override
|
||||
public void logError(Component component) {
|
||||
componentLogger.error(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logInfo(Component component) {
|
||||
componentLogger.info(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object platformServerInstance() {
|
||||
return this.minecraftServer;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package io.github.miniplaceholders.fabric;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* Based on the MIT licensed Paper-Server patch "Further improve server tick loop".
|
||||
*
|
||||
* @author Daniel Ennis/Aikar
|
||||
*/
|
||||
final class RollingAverage {
|
||||
private static final DecimalFormat TPS_FORMAT = new DecimalFormat("###.##");
|
||||
static { TPS_FORMAT.setRoundingMode(RoundingMode.HALF_UP); }
|
||||
private static final int TPS = 20;
|
||||
private static final long SEC_IN_NANO = 1000000000;
|
||||
private final int size;
|
||||
private final BigDecimal[] samples;
|
||||
private final long[] times;
|
||||
private long time;
|
||||
private BigDecimal total;
|
||||
private int index = 0;
|
||||
|
||||
RollingAverage(final int size) {
|
||||
this.size = size;
|
||||
this.samples = new BigDecimal[size];
|
||||
this.times = new long[size];
|
||||
this.time = size * SEC_IN_NANO;
|
||||
this.total = dec(TPS).multiply(dec(SEC_IN_NANO)).multiply(dec(size));
|
||||
for (int i = 0; i < size; i++) {
|
||||
this.samples[i] = dec(TPS);
|
||||
this.times[i] = SEC_IN_NANO;
|
||||
}
|
||||
}
|
||||
|
||||
private static BigDecimal dec(final long t) {
|
||||
return new BigDecimal(t);
|
||||
}
|
||||
|
||||
void add(final BigDecimal x, final long t) {
|
||||
this.time -= this.times[this.index];
|
||||
this.total = this.total.subtract(this.samples[this.index].multiply(dec(this.times[this.index])));
|
||||
this.samples[this.index] = x;
|
||||
this.times[this.index] = t;
|
||||
this.time += t;
|
||||
this.total = this.total.add(x.multiply(dec(t)));
|
||||
if (++this.index == this.size) {
|
||||
this.index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
double average() {
|
||||
return this.total.divide(dec(this.time), 30, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
String formattedAverage() {
|
||||
return TPS_FORMAT.format(average());
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package io.github.miniplaceholders.fabric;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
final class TickManager {
|
||||
private static final int TICK_DURATION = 20;
|
||||
|
||||
private static final BigDecimal TPS_BASE = new BigDecimal("1E9").multiply(new BigDecimal(TICK_DURATION));
|
||||
|
||||
private final RollingAverage tps1m = new RollingAverage(60);
|
||||
private final RollingAverage tps5m = new RollingAverage(60 * 5);
|
||||
private final RollingAverage tps15m = new RollingAverage(60 * 15);
|
||||
private long lastStartTick = 0;
|
||||
private long lastEndTick = 0;
|
||||
|
||||
TickManager() {
|
||||
ServerTickEvents.START_SERVER_TICK.register(server -> this.lastStartTick = System.nanoTime());
|
||||
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
if (server.getTickCount() % TICK_DURATION == 0) {
|
||||
final long diff = lastStartTick - lastEndTick;
|
||||
final BigDecimal currentTps = TPS_BASE.divide(new BigDecimal(diff), 30, RoundingMode.HALF_UP);
|
||||
this.tps1m.add(currentTps, diff);
|
||||
this.tps5m.add(currentTps, diff);
|
||||
this.tps15m.add(currentTps, diff);
|
||||
this.lastEndTick = System.nanoTime();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public RollingAverage getTps1m() {
|
||||
return tps1m;
|
||||
}
|
||||
|
||||
public RollingAverage getTps5m() {
|
||||
return tps5m;
|
||||
}
|
||||
|
||||
public RollingAverage getTps15m() {
|
||||
return tps15m;
|
||||
}
|
||||
}
|
||||
@@ -3,27 +3,28 @@
|
||||
"id": "miniplaceholders",
|
||||
"version": "${version}",
|
||||
"name": "MiniPlaceholders",
|
||||
"description": "MiniMessage Component-based Placeholders for PaperMC, Fabric, Krypton and Velocity platforms",
|
||||
"description": "MiniMessage Component-based Placeholders API",
|
||||
"authors": [
|
||||
"4drian3d"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://github.com/MiniPlaceholders/MiniPlaceholders",
|
||||
"homepage": "https://miniplaceholders.github.io/",
|
||||
"sources": "https://github.com/MiniPlaceholders/MiniPlaceholders",
|
||||
"issues": "https://github.com/MiniPlaceholders/MiniPlaceholders/issues"
|
||||
},
|
||||
"license": "Apache-2",
|
||||
"environment": "server",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"server": [
|
||||
"io.github.miniplaceholders.fabric.FabricMod"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"fabricloader": ">=0.14.19",
|
||||
"fabric": "*",
|
||||
"java": ">=17",
|
||||
"minecraft": ">=1.20.2",
|
||||
"fabric-permissions-api-v0": "*"
|
||||
"java": ">=21",
|
||||
"adventure-platform-fabric": "~6.6.0",
|
||||
"minecraft": ">=1.21.6 <=1.21.8",
|
||||
"fabric-permissions-api-v0": "~0.4.1"
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -1,5 +1,8 @@
|
||||
group = io.github.miniplaceholders
|
||||
version = 2.2.4
|
||||
description = MiniMessage Component-based Placeholders for Paper, Fabric, Sponge, Folia and Velocity platforms
|
||||
version = 3.1.0-SNAPSHOT
|
||||
description = MiniMessage Component-based Placeholders API
|
||||
|
||||
org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.jvmargs=-Xmx2G
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
|
||||
#org.gradle.configuration-cache=true
|
||||
+24
-39
@@ -2,27 +2,22 @@ metadata.format.version = "1.1"
|
||||
|
||||
[versions]
|
||||
|
||||
velocity = "3.2.0-SNAPSHOT"
|
||||
paper = "1.20.2-R0.1-SNAPSHOT"
|
||||
minecraft = "1.20.2"
|
||||
velocity = "3.4.0-SNAPSHOT"
|
||||
paper = "1.21.8-R0.1-SNAPSHOT"
|
||||
minecraft = "1.21.8"
|
||||
|
||||
runtask = "2.2.2"
|
||||
pluginyml = "0.6.0"
|
||||
userdev = "1.5.11"
|
||||
runtask = "3.0.0"
|
||||
blossom = "2.1.0"
|
||||
indra = "3.1.3"
|
||||
shadow = "8.1.1"
|
||||
jmh = "0.7.2"
|
||||
dokka = "1.9.10"
|
||||
shadow = "9.2.2"
|
||||
jmh = "0.7.3"
|
||||
dokka = "2.0.0"
|
||||
|
||||
cloud = "1.8.4"
|
||||
adventure = "4.15.0"
|
||||
adventurefabric = "5.11.0"
|
||||
brigadier = "1.0.500"
|
||||
kotlin="1.9.22"
|
||||
adventure = "4.24.0"
|
||||
adventureModded = "6.6.0"
|
||||
kotlin = "2.2.20"
|
||||
|
||||
fabric-api = "0.93.1+1.20.5"
|
||||
fabric-loader = "0.15.6"
|
||||
fabric-api = "0.133.14+1.21.9"
|
||||
fabric-loader = "0.17.2"
|
||||
|
||||
desertwell = "2.0.4"
|
||||
|
||||
@@ -34,42 +29,32 @@ minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft
|
||||
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }
|
||||
luckopermissionsapi = { group = "me.lucko", name = "fabric-permissions-api", version = "0.2-SNAPSHOT" }
|
||||
luckopermissionsapi = { group = "me.lucko", name = "fabric-permissions-api", version = "0.4.1" }
|
||||
|
||||
adventure-api = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" }
|
||||
adventure-minimesssage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" }
|
||||
adventure-platform-fabric = { group = "net.kyori", name = "adventure-platform-fabric", version.ref = "adventurefabric" }
|
||||
adventure-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" }
|
||||
adventure-platform-fabric = { group = "net.kyori", name = "adventure-platform-fabric", version.ref = "adventureModded" }
|
||||
adventure-serializer-legacy = { group = "net.kyori", name = "adventure-text-serializer-legacy", version.ref = "adventure" }
|
||||
adventure-serializer-plain = { group = "net.kyori", name = "adventure-text-serializer-plain", version.ref = "adventure" }
|
||||
|
||||
cloud-core = { group = "cloud.commandframework", name = "cloud-core", version.ref = "cloud" }
|
||||
cloud-annotations = { group = "cloud.commandframework", name = "cloud-annotations", version.ref = "cloud" }
|
||||
cloud-velocity = { group = "cloud.commandframework", name = "cloud-velocity", version.ref = "cloud" }
|
||||
cloud-paper = { group = "cloud.commandframework", name = "cloud-paper", version.ref = "cloud" }
|
||||
cloud-fabric = { group = "cloud.commandframework", name = "cloud-fabric", version.ref = "cloud" }
|
||||
cloud-sponge = { group = "cloud.commandframework", name = "cloud-sponge", version = "1.8.0-SNAPSHOT" }
|
||||
cloud-extras = { group = "cloud.commandframework", name = "cloud-minecraft-extras", version.ref = "cloud" }
|
||||
|
||||
brigadier = { group = "com.mojang", name = "brigadier", version.ref = "brigadier" }
|
||||
|
||||
plugin-shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
|
||||
plugin-shadow = { module = "com.gradleup.shadow:shadow-gradle-plugin", version.ref = "shadow" }
|
||||
plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
plugin-indra-common = { module = "net.kyori:indra-common", version.ref = "indra" }
|
||||
plugin-indra-spotless = { module = "net.kyori:indra-licenser-spotless", version.ref = "indra" }
|
||||
plugin-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:1.9.10"
|
||||
plugin-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
|
||||
|
||||
desertwell = { group = "net.william278", name = "DesertWell", version.ref = "desertwell" }
|
||||
|
||||
junit-bom = "org.junit:junit-bom:5.10.1"
|
||||
junit-bom = "org.junit:junit-bom:6.0.0"
|
||||
jspecify = "org.jspecify:jspecify:1.0.0"
|
||||
|
||||
unnamedinject = "team.unnamed:inject:2.0.1"
|
||||
brigadier = "com.mojang:brigadier:1.0.500"
|
||||
|
||||
[plugins]
|
||||
|
||||
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
|
||||
userdev = { id = "io.papermc.paperweight.userdev", version.ref = "userdev" }
|
||||
runpaper = { id = "xyz.jpenilla.run-paper", version.ref = "runtask" }
|
||||
runvelocity = { id = "xyz.jpenilla.run-velocity", version.ref = "runtask" }
|
||||
blossom = { id = "net.kyori.blossom", version.ref = "blossom" }
|
||||
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
|
||||
jmh = { id = "me.champeau.jmh", version.ref = "jmh" }
|
||||
idea-ext = { id = "org.jetbrains.gradle.plugin.idea-ext", version = "1.3" }
|
||||
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
|
||||
idea-ext = { id = "org.jetbrains.gradle.plugin.idea-ext", version = "1.1.7" }
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -84,7 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -112,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -170,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -203,15 +203,14 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
Vendored
+13
-12
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,22 +59,21 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# Package io.github.miniplaceholders.kotlin
|
||||
@@ -1,19 +1,87 @@
|
||||
import com.vanniktech.maven.publish.KotlinJvm
|
||||
import com.vanniktech.maven.publish.JavadocJar
|
||||
import java.time.LocalDate
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("miniplaceholders.kotlin.publish")
|
||||
id("com.vanniktech.maven.publish")
|
||||
id("org.jetbrains.dokka") version "2.0.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(kotlin("stdlib"))
|
||||
compileOnly(libs.adventure.api)
|
||||
compileOnly(libs.adventure.minimesssage)
|
||||
compileOnlyApi(projects.miniplaceholdersApi)
|
||||
compileOnly(libs.adventure.minimessage)
|
||||
compileOnly(projects.miniplaceholdersApi)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
jvmToolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dokka {
|
||||
dokkaSourceSets.main {
|
||||
includes.from("README.md")
|
||||
sourceLink {
|
||||
localDirectory.set(rootDir)
|
||||
remoteUrl("https://github.com/MiniPlaceholders/MiniPlaceholders/tree/main")
|
||||
remoteLineSuffix.set("#L")
|
||||
}
|
||||
}
|
||||
pluginsConfiguration.html {
|
||||
footerMessage.set("(c) MiniPlaceholders | 4drian3d")
|
||||
}
|
||||
}
|
||||
|
||||
mavenPublishing {
|
||||
publishToMavenCentral()
|
||||
signAllPublications()
|
||||
coordinates(project.group as String, "miniplaceholders-kotlin-ext", project.version as String)
|
||||
pom {
|
||||
name.set(project.name)
|
||||
description.set(project.description)
|
||||
inceptionYear.set(LocalDate.now().year.toString())
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
licenses {
|
||||
license {
|
||||
name.set("The Apache License, Version 2.0")
|
||||
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("4drian3d")
|
||||
name.set("Adrian Gonzales")
|
||||
email.set("adriangonzalesval@gmail.com")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:https://github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
developerConnection.set("scm:git:ssh://git@github.com/MiniPlaceholders/MiniPlaceholders.git")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders")
|
||||
}
|
||||
ciManagement {
|
||||
name.set("GitHub Actions")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/actions")
|
||||
}
|
||||
issueManagement {
|
||||
name.set("GitHub")
|
||||
url.set("https://github.com/MiniPlaceholders/MiniPlaceholders/issues")
|
||||
}
|
||||
}
|
||||
configure(KotlinJvm(
|
||||
// configures the -javadoc artifact, possible values:
|
||||
// - `JavadocJar.None()` don't publish this artifact
|
||||
// - `JavadocJar.Empty()` publish an empty jar
|
||||
// - `JavadocJar.Dokka("dokkaHtml")` when using Kotlin with Dokka, where `dokkaHtml` is the name of the Dokka task that should be used as input
|
||||
javadocJar = JavadocJar.Dokka("dokkaGeneratePublicationHtml"),
|
||||
sourcesJar = true,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package io.github.miniplaceholders.kotlin
|
||||
|
||||
import io.github.miniplaceholders.api.Expansion
|
||||
import io.github.miniplaceholders.api.resolver.AudienceTagResolver
|
||||
import io.github.miniplaceholders.api.resolver.GlobalTagResolver
|
||||
import io.github.miniplaceholders.api.resolver.RelationalTagResolver
|
||||
import net.kyori.adventure.audience.Audience
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag
|
||||
|
||||
/**
|
||||
* Create a new Expansion
|
||||
@@ -12,5 +17,23 @@ import io.github.miniplaceholders.api.Expansion
|
||||
* @see Expansion.builder
|
||||
* @see Expansion.Builder.build
|
||||
* */
|
||||
public fun expansion(name: String, builder: Expansion.Builder.() -> Expansion.Builder): Expansion
|
||||
= builder(Expansion.builder(name)).build()
|
||||
public fun expansion(name: String, builder: Expansion.Builder.() -> Expansion.Builder): Expansion =
|
||||
builder(Expansion.builder(name)).build()
|
||||
|
||||
public fun Expansion.Builder.global(placeholderName: String, resolver: GlobalTagResolver): Expansion.Builder =
|
||||
this.globalPlaceholder(placeholderName, resolver)
|
||||
|
||||
public fun Expansion.Builder.global(placeholderName: String, tag: Tag): Expansion.Builder =
|
||||
this.globalPlaceholder(placeholderName, tag)
|
||||
|
||||
public inline fun <reified A : Audience> Expansion.Builder.audience(
|
||||
placeholderName: String,
|
||||
resolver: AudienceTagResolver<A>
|
||||
): Expansion.Builder =
|
||||
this.audiencePlaceholder(A::class.java, placeholderName, resolver)
|
||||
|
||||
public inline fun <reified A : Audience> Expansion.Builder.relational(
|
||||
placeholderName: String,
|
||||
resolver: RelationalTagResolver<A>
|
||||
): Expansion.Builder =
|
||||
this.relationalPlaceholder(A::class.java, placeholderName, resolver)
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.github.miniplaceholders.kotlin
|
||||
|
||||
import io.github.miniplaceholders.api.utils.LegacyStrings
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.minimessage.Context
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver
|
||||
|
||||
public fun String.hasLegacyFormat() : Boolean = LegacyStrings.hasLegacyFormat(this)
|
||||
|
||||
public fun String.parsePossibleLegacy(resolver: TagResolver = TagResolver.empty()) : Component =
|
||||
LegacyStrings.parsePossibleLegacy(this, resolver)
|
||||
|
||||
public fun String.parsePossibleLegacy(context: Context) : Component =
|
||||
LegacyStrings.parsePossibleLegacy(this, context)
|
||||
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user