Skip to content

Commit 4f24cba

Browse files
committed
Added PlaceholderAPI support
Implemented placeholders for chat, economy, and groups by integrating PlaceholderAPI and registering corresponding expansions. Updated build scripts with required dependencies.
1 parent 025a9ff commit 4f24cba

File tree

7 files changed

+237
-0
lines changed

7 files changed

+237
-0
lines changed

plugin/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ repositories {
2727
maven("https://jitpack.io")
2828
maven("https://maven.citizensnpcs.co/repo")
2929
maven("https://repo.alessiodp.com/releases")
30+
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
3031
maven("https://repo.fancyplugins.de/releases")
3132
maven("https://repo.papermc.io/repository/maven-public/")
3233
maven("https://repo.thenextlvl.net/releases")
@@ -39,6 +40,7 @@ dependencies {
3940
compileOnly("com.github.decentsoftware-eu:decentholograms:2.9.4")
4041
compileOnly("de.oliver:FancyHolograms:2.6.0")
4142
compileOnly("de.oliver:FancyNpcs:2.6.0")
43+
compileOnly("me.clip:placeholderapi:2.11.6")
4244
compileOnly("net.citizensnpcs:citizens-main:2.0.39-SNAPSHOT")
4345
compileOnly("net.luckperms:api:5.5")
4446

@@ -99,6 +101,10 @@ paper {
99101
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
100102
required = false
101103
}
104+
register("PlaceholderAPI") {
105+
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
106+
required = false
107+
}
102108
}
103109

104110
permissions {

plugin/src/main/java/net/thenextlvl/service/ServicePlugin.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import net.thenextlvl.service.controller.permission.SuperPermsPermissionController;
3030
import net.thenextlvl.service.listener.CitizensListener;
3131
import net.thenextlvl.service.listener.FancyNpcsListener;
32+
import net.thenextlvl.service.placeholder.chat.ServiceChatPlaceholderExpansion;
33+
import net.thenextlvl.service.placeholder.economy.ServiceEconomyPlaceholderExpansion;
34+
import net.thenextlvl.service.placeholder.group.ServiceGroupPlaceholderExpansion;
3235
import net.thenextlvl.service.version.PluginVersionChecker;
3336
import net.thenextlvl.service.wrapper.VaultChatServiceWrapper;
3437
import net.thenextlvl.service.wrapper.VaultEconomyServiceWrapper;
@@ -94,9 +97,18 @@ public void onEnable() {
9497
loadVaultEconomyWrapper();
9598
loadVaultChatWrapper();
9699

100+
registerPlaceholders();
101+
97102
addCustomCharts();
98103
}
99104

105+
private void registerPlaceholders() {
106+
if (!getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) return;
107+
new ServiceChatPlaceholderExpansion(this).register();
108+
new ServiceEconomyPlaceholderExpansion(this).register();
109+
new ServiceGroupPlaceholderExpansion(this).register();
110+
}
111+
100112
public ComponentBundle bundle() {
101113
return bundle;
102114
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package net.thenextlvl.service.placeholder;
2+
3+
import com.google.common.base.Preconditions;
4+
import org.bukkit.OfflinePlayer;
5+
import org.jspecify.annotations.NullMarked;
6+
import org.jspecify.annotations.Nullable;
7+
8+
import java.util.regex.Matcher;
9+
10+
@NullMarked
11+
public interface PlaceholderResolver {
12+
@Nullable
13+
String resolve(OfflinePlayer player, Matcher matcher) throws RuntimeException;
14+
15+
@SafeVarargs
16+
static PlaceholderResolver throwing(PlaceholderResolver resolver, Class<? extends RuntimeException>... ignored) {
17+
Preconditions.checkArgument(ignored.length > 0, "At least one exception class must be provided");
18+
return (player, matcher) -> {
19+
try {
20+
return resolver.resolve(player, matcher);
21+
} catch (RuntimeException exception) {
22+
for (var clazz : ignored) {
23+
if (clazz.isInstance(exception)) return null;
24+
}
25+
throw exception;
26+
}
27+
};
28+
}
29+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package net.thenextlvl.service.placeholder;
2+
3+
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
4+
import net.thenextlvl.service.ServicePlugin;
5+
import org.bukkit.OfflinePlayer;
6+
import org.jspecify.annotations.NullMarked;
7+
import org.jspecify.annotations.Nullable;
8+
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.regex.Pattern;
13+
14+
@NullMarked
15+
public abstract class ServicePlaceholderExpansion<T> extends PlaceholderExpansion {
16+
private final Map<Pattern, PlaceholderResolver> resolvers = new HashMap<>();
17+
private final String identifier;
18+
private final @Nullable T provider;
19+
20+
protected final ServicePlugin plugin;
21+
22+
protected ServicePlaceholderExpansion(ServicePlugin plugin, String identifier, Class<T> providerClass) {
23+
this.plugin = plugin;
24+
this.identifier = identifier;
25+
this.provider = plugin.getServer().getServicesManager().load(providerClass);
26+
if (provider != null) registerResolvers(provider);
27+
}
28+
29+
protected ServicePlaceholderExpansion(ServicePlugin plugin, Class<T> providerClass) {
30+
this(plugin, "serviceio", providerClass);
31+
}
32+
33+
@Override
34+
public final String getIdentifier() {
35+
return identifier;
36+
}
37+
38+
@Override
39+
public final String getAuthor() {
40+
return String.join(", ", getAuthors());
41+
}
42+
43+
protected List<String> getAuthors() {
44+
return plugin.getPluginMeta().getAuthors();
45+
}
46+
47+
@Override
48+
public String getVersion() {
49+
return plugin.getPluginMeta().getVersion();
50+
}
51+
52+
@Override
53+
public final boolean persist() {
54+
return true;
55+
}
56+
57+
@Override
58+
public final @Nullable String onRequest(@Nullable OfflinePlayer player, String params) {
59+
try {
60+
if (player == null || provider == null) return null;
61+
for (var entry : resolvers.entrySet()) {
62+
var matcher = entry.getKey().matcher(params);
63+
if (!matcher.matches()) continue;
64+
return entry.getValue().resolve(player, matcher);
65+
}
66+
return null;
67+
} catch (Exception e) {
68+
var name = player.getName() != null ? player.getName() : player.getUniqueId().toString();
69+
plugin.getComponentLogger().warn("Failed to resolve placeholder '{}' for player {}", params, name, e);
70+
return null;
71+
}
72+
}
73+
74+
protected final void registerResolver(Pattern pattern, PlaceholderResolver resolver) {
75+
resolvers.put(pattern, resolver);
76+
}
77+
78+
protected final void registerResolver(String regex, PlaceholderResolver resolver) {
79+
resolvers.put(Pattern.compile(regex.replace("%s", "([^{}_]+)")), resolver);
80+
}
81+
82+
protected abstract void registerResolvers(T provider);
83+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.thenextlvl.service.placeholder.chat;
2+
3+
import net.thenextlvl.service.ServicePlugin;
4+
import net.thenextlvl.service.api.chat.ChatController;
5+
import net.thenextlvl.service.api.model.Display;
6+
import net.thenextlvl.service.placeholder.ServicePlaceholderExpansion;
7+
import org.jspecify.annotations.NullMarked;
8+
9+
@NullMarked
10+
public class ServiceChatPlaceholderExpansion extends ServicePlaceholderExpansion<ChatController> {
11+
public ServiceChatPlaceholderExpansion(ServicePlugin plugin) {
12+
super(plugin, ChatController.class);
13+
}
14+
15+
@Override
16+
protected void registerResolvers(ChatController provider) {
17+
// %serviceio_prefix%
18+
registerResolver("prefix", (player, matcher) -> {
19+
return provider.getProfile(player).flatMap(Display::getPrefix).orElse("");
20+
});
21+
22+
// %serviceio_suffix%
23+
registerResolver("suffix", (player, matcher) -> {
24+
return provider.getProfile(player).flatMap(Display::getSuffix).orElse("");
25+
});
26+
27+
// %serviceio_displayname%
28+
registerResolver("displayname", (player, matcher) -> {
29+
return provider.getProfile(player).flatMap(Display::getDisplayName).orElse(player.getName());
30+
});
31+
}
32+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package net.thenextlvl.service.placeholder.economy;
2+
3+
import net.thenextlvl.service.ServicePlugin;
4+
import net.thenextlvl.service.api.economy.Account;
5+
import net.thenextlvl.service.api.economy.EconomyController;
6+
import net.thenextlvl.service.placeholder.ServicePlaceholderExpansion;
7+
import org.jspecify.annotations.NullMarked;
8+
9+
import java.math.BigDecimal;
10+
11+
@NullMarked
12+
public class ServiceEconomyPlaceholderExpansion extends ServicePlaceholderExpansion<EconomyController> {
13+
public ServiceEconomyPlaceholderExpansion(ServicePlugin plugin) {
14+
super(plugin, EconomyController.class);
15+
}
16+
17+
@Override
18+
protected void registerResolvers(EconomyController provider) {
19+
// %serviceio_balance%
20+
registerResolver("balance", (player, matcher) -> {
21+
return provider.getAccount(player).map(Account::getBalance).orElse(BigDecimal.ZERO).toPlainString();
22+
});
23+
24+
// %serviceio_balance_<world>%
25+
registerResolver("balance_%s", (player, matcher) -> {
26+
var world = plugin.getServer().getWorld(matcher.group(1));
27+
if (world == null) return null;
28+
return provider.getAccount(player, world).map(Account::getBalance).orElse(BigDecimal.ZERO).toPlainString();
29+
});
30+
31+
// %serviceio_balance_formatted%
32+
registerResolver("balance_formatted", (player, matcher) -> {
33+
return provider.format(provider.getAccount(player).map(Account::getBalance).orElse(BigDecimal.ZERO));
34+
});
35+
36+
// %serviceio_balance_formatted%
37+
registerResolver("balance_formatted_%s", (player, matcher) -> {
38+
var world = plugin.getServer().getWorld(matcher.group(1));
39+
if (world == null) return null;
40+
return provider.format(provider.getAccount(player, world).map(Account::getBalance).orElse(BigDecimal.ZERO));
41+
});
42+
}
43+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.thenextlvl.service.placeholder.group;
2+
3+
import net.thenextlvl.service.ServicePlugin;
4+
import net.thenextlvl.service.api.group.Group;
5+
import net.thenextlvl.service.api.group.GroupController;
6+
import net.thenextlvl.service.api.group.GroupHolder;
7+
import net.thenextlvl.service.placeholder.ServicePlaceholderExpansion;
8+
import org.jspecify.annotations.NullMarked;
9+
10+
import java.util.stream.Collectors;
11+
12+
@NullMarked
13+
public class ServiceGroupPlaceholderExpansion extends ServicePlaceholderExpansion<GroupController> {
14+
public ServiceGroupPlaceholderExpansion(ServicePlugin plugin) {
15+
super(plugin, GroupController.class);
16+
}
17+
18+
@Override
19+
protected void registerResolvers(GroupController provider) {
20+
// %serviceio_group%
21+
registerResolver("group", (player, matcher) -> {
22+
return provider.getGroupHolder(player).map(GroupHolder::getPrimaryGroup).orElse("");
23+
});
24+
25+
// %serviceio_groups%
26+
registerResolver("groups", (player, matcher) -> {
27+
return provider.getGroupHolder(player).map(GroupHolder::getGroups).map(groups ->
28+
groups.stream().map(Group::getName).collect(Collectors.joining(", "))
29+
).orElse("");
30+
});
31+
}
32+
}

0 commit comments

Comments
 (0)