Skip to content

Commit b6f5b1c

Browse files
authored
Merge pull request #112 from TheNextLvl-net/placeholderapi
Added PlaceholderAPI support
2 parents 9b35c12 + 47e77ec commit b6f5b1c

File tree

11 files changed

+348
-7
lines changed

11 files changed

+348
-7
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.5")
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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
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.ServiceBankPlaceholderExpansion;
34+
import net.thenextlvl.service.placeholder.economy.ServiceEconomyPlaceholderExpansion;
35+
import net.thenextlvl.service.placeholder.group.ServiceGroupPlaceholderExpansion;
3236
import net.thenextlvl.service.version.PluginVersionChecker;
3337
import net.thenextlvl.service.wrapper.VaultChatServiceWrapper;
3438
import net.thenextlvl.service.wrapper.VaultEconomyServiceWrapper;
@@ -94,9 +98,19 @@ public void onEnable() {
9498
loadVaultEconomyWrapper();
9599
loadVaultChatWrapper();
96100

101+
registerPlaceholders();
102+
97103
addCustomCharts();
98104
}
99105

106+
private void registerPlaceholders() {
107+
if (getServer().getPluginManager().getPlugin("PlaceholderAPI") == null) return;
108+
new ServiceBankPlaceholderExpansion(this).register();
109+
new ServiceChatPlaceholderExpansion(this).register();
110+
new ServiceEconomyPlaceholderExpansion(this).register();
111+
new ServiceGroupPlaceholderExpansion(this).register();
112+
}
113+
100114
public ComponentBundle bundle() {
101115
return bundle;
102116
}
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: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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.Locale;
12+
import java.util.Map;
13+
import java.util.regex.Pattern;
14+
15+
@NullMarked
16+
public abstract class ServicePlaceholderExpansion<T> extends PlaceholderExpansion {
17+
private final Map<Pattern, PlaceholderResolver> resolvers = new HashMap<>();
18+
private final String identifier;
19+
private final @Nullable T provider;
20+
21+
protected final ServicePlugin plugin;
22+
23+
protected ServicePlaceholderExpansion(ServicePlugin plugin, String identifier, Class<T> providerClass) {
24+
this.plugin = plugin;
25+
this.identifier = identifier;
26+
this.provider = plugin.getServer().getServicesManager().load(providerClass);
27+
if (provider == null) return;
28+
registerResolvers(provider);
29+
var name = provider.getClass().getSimpleName().toLowerCase(Locale.ROOT);
30+
plugin.getComponentLogger().info("Registered placeholder expansion for '{}:{}'", identifier, name);
31+
}
32+
33+
protected ServicePlaceholderExpansion(ServicePlugin plugin, Class<T> providerClass) {
34+
this(plugin, "serviceio", providerClass);
35+
}
36+
37+
@Override
38+
public boolean canRegister() {
39+
return provider != null && super.canRegister();
40+
}
41+
42+
@Override
43+
public final String getIdentifier() {
44+
return identifier;
45+
}
46+
47+
@Override
48+
public final String getAuthor() {
49+
return String.join(", ", getAuthors());
50+
}
51+
52+
protected List<String> getAuthors() {
53+
return plugin.getPluginMeta().getAuthors();
54+
}
55+
56+
@Override
57+
public String getVersion() {
58+
return plugin.getPluginMeta().getVersion();
59+
}
60+
61+
@Override
62+
public final boolean persist() {
63+
return true;
64+
}
65+
66+
@Override
67+
public final @Nullable String onRequest(@Nullable OfflinePlayer player, String params) {
68+
try {
69+
if (player == null || provider == null) return null;
70+
for (var entry : resolvers.entrySet()) {
71+
var matcher = entry.getKey().matcher(params);
72+
if (!matcher.matches()) continue;
73+
return entry.getValue().resolve(player, matcher);
74+
}
75+
return null;
76+
} catch (Exception e) {
77+
var name = player.getName() != null ? player.getName() : player.getUniqueId().toString();
78+
plugin.getComponentLogger().warn("Failed to resolve placeholder '{}' for player {}", params, name, e);
79+
return null;
80+
}
81+
}
82+
83+
protected final void registerResolver(Pattern pattern, PlaceholderResolver resolver) {
84+
resolvers.put(pattern, resolver);
85+
}
86+
87+
protected final void registerResolver(String regex, PlaceholderResolver resolver) {
88+
resolvers.put(Pattern.compile(regex.replace("%s", "([^{}_]+)")), resolver);
89+
}
90+
91+
protected abstract void registerResolvers(T provider);
92+
}
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: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package net.thenextlvl.service.placeholder.economy;
2+
3+
import net.thenextlvl.service.ServicePlugin;
4+
import net.thenextlvl.service.api.economy.bank.Bank;
5+
import net.thenextlvl.service.api.economy.bank.BankController;
6+
import net.thenextlvl.service.placeholder.ServicePlaceholderExpansion;
7+
import org.jspecify.annotations.NullMarked;
8+
9+
import java.math.BigDecimal;
10+
import java.util.stream.Collectors;
11+
12+
@NullMarked
13+
public class ServiceBankPlaceholderExpansion extends ServicePlaceholderExpansion<BankController> {
14+
public ServiceBankPlaceholderExpansion(ServicePlugin plugin) {
15+
super(plugin, BankController.class);
16+
}
17+
18+
@Override
19+
protected void registerResolvers(BankController provider) {
20+
// %serviceio_bank%
21+
registerResolver("bank", (player, matcher) -> {
22+
return provider.getBank(player).map(Bank::getName).orElse("");
23+
});
24+
25+
// %serviceio_bank_balance%
26+
registerResolver("bank_balance", (player, matcher) -> {
27+
return provider.getBank(player).map(Bank::getBalance).orElse(BigDecimal.ZERO).toPlainString();
28+
});
29+
30+
// %serviceio_bank_balance_formatted%
31+
registerResolver("bank_balance_formatted", (player, matcher) -> {
32+
return provider.format(provider.getBank(player).map(Bank::getBalance).orElse(BigDecimal.ZERO));
33+
});
34+
35+
// %serviceio_bank_<world>%
36+
registerResolver("bank_%s", (player, matcher) -> {
37+
var world = plugin.getServer().getWorld(matcher.group(1));
38+
if (world == null) return null;
39+
return provider.getBank(player, world).map(Bank::getName).orElse("");
40+
});
41+
42+
// %serviceio_bank_<world>_balance%
43+
registerResolver("bank_%s_balance", (player, matcher) -> {
44+
var world = plugin.getServer().getWorld(matcher.group(1));
45+
if (world == null) return null;
46+
return provider.getBank(player, world).map(Bank::getBalance).orElse(BigDecimal.ZERO).toPlainString();
47+
});
48+
49+
// %serviceio_bank_<world>_balance_formatted%
50+
registerResolver("bank_%s_balance_formatted", (player, matcher) -> {
51+
var world = plugin.getServer().getWorld(matcher.group(1));
52+
if (world == null) return null;
53+
return provider.format(provider.getBank(player, world).map(Bank::getBalance).orElse(BigDecimal.ZERO));
54+
});
55+
56+
// %serviceio_banks%
57+
registerResolver("banks", (player, matcher) -> {
58+
return provider.getBanks().stream().map(Bank::getName).collect(Collectors.joining(", "));
59+
});
60+
61+
// %serviceio_banks_<world>%
62+
registerResolver("banks_%s", (player, matcher) -> {
63+
var world = plugin.getServer().getWorld(matcher.group(1));
64+
if (world == null) return null;
65+
return provider.getBanks(world).stream().map(Bank::getName).collect(Collectors.joining(", "));
66+
});
67+
}
68+
}
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+
}

plugin/src/main/java/net/thenextlvl/service/wrapper/service/BankServiceWrapper.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ public BankServiceWrapper(Economy economy, Plugin provider, ServicePlugin plugin
3030
this.provider = provider;
3131
}
3232

33+
@Override
34+
public String format(Number amount) {
35+
return economy.format(amount.doubleValue());
36+
}
37+
38+
@Override
39+
public int fractionalDigits() {
40+
return economy.fractionalDigits();
41+
}
42+
3343
@Override
3444
public CompletableFuture<Bank> createBank(OfflinePlayer player, String name) throws IllegalStateException {
3545
return CompletableFuture.supplyAsync(() -> economy.createBank(name, player))

0 commit comments

Comments
 (0)