diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index cf247ce..0000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
deleted file mode 100644
index d4b7acc..0000000
--- a/.idea/kotlinc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index ecb0c83..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/core__runServer_.xml b/.idea/runConfigurations/core__runServer_.xml
deleted file mode 100644
index 4d1d34f..0000000
--- a/.idea/runConfigurations/core__runServer_.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- true
- true
- false
- false
-
-
-
\ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 8296765..0000000
--- a/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-all:
- ./gradlew build
-
-install:
- cp core-bukkit-impl/build/libs/core-bukkit-impl-all.jar /var/scrow/ /var/scrow/
diff --git a/README.md b/README.md
index c64c694..53ec4a9 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
TODO
-- fix kencommandapi greedy strings & arguments: probably issue with shading
- make core-lib-impl: a library -> avoid shading massive core-lib
- logging in general, log some exceptions to game chat
- notification: Friend is now offline/online
@@ -12,4 +11,4 @@ modules
- **core-bukkit-api** stateful api, for minecraft development, depends on core-lib
- **core-bukkit-api** stateful plugin, same as core-bukkit-api but implements it
- **core-velocity-api** stateful api, for proxy development, depends on core-lib
-- **core-velocity-impl** stateful plugin, same as core-velocity-api but implements it
+- **core-velocity-impl** stateful plugin, same as core-velocity-api but implements it
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 3b40b84..605fa25 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -7,4 +7,8 @@ subprojects {
tasks.withType {
archiveVersion = ""
}
+
+ configurations.configureEach {
+ resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
+ }
}
\ No newline at end of file
diff --git a/core-bukkit-api/build.gradle.kts b/core-bukkit-api/build.gradle.kts
index 9c64d47..85a7666 100644
--- a/core-bukkit-api/build.gradle.kts
+++ b/core-bukkit-api/build.gradle.kts
@@ -13,7 +13,9 @@ dependencies {
compileOnly(libs.papermc.api)
api(libs.nats)
- api(libs.scrow.commandapi.bukkit)
+ api(libs.scrow.commandapi.bukkit) {
+ isChanging = true
+ }
}
publishing {
diff --git a/core-bukkit-impl/build.gradle.kts b/core-bukkit-impl/build.gradle.kts
index b0d7520..c033fb6 100644
--- a/core-bukkit-impl/build.gradle.kts
+++ b/core-bukkit-impl/build.gradle.kts
@@ -14,8 +14,12 @@ dependencies {
implementation(libs.bson)
implementation(libs.nats)
- implementation(libs.scrow.commandapi.bukkit)
+ implementation(libs.scrow.commandapi.bukkit) {
+ isChanging = true
+ }
implementation(libs.postgres)
+
+ compileOnly(libs.luckperms)
}
tasks {
diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java
index acda52c..5c9f156 100644
--- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java
+++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java
@@ -1,12 +1,13 @@
package de.kentoj.scrow.bukkit;
+import de.kentoj.scrow.bukkit.chatstyle.ChatStyleListener;
+import de.kentoj.scrow.bukkit.chatstyle.NoPrefixPrefixCache;
import de.kentoj.scrow.bukkit.command.economy.CoinsCommand;
import de.kentoj.scrow.bukkit.command.friends.FriendCommand;
import de.kentoj.scrow.bukkit.command.instance.InstanceCache;
import de.kentoj.scrow.bukkit.command.instance.InstanceCommand;
import de.kentoj.scrow.bukkit.command.instance.LobbyCommand;
import de.kentoj.scrow.bukkit.command.instance.PlayCommand;
-import de.kentoj.scrow.bukkit.listener.ChatStyleListener;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
@@ -15,7 +16,7 @@ import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
-public class CoreImplPlugin extends JavaPlugin {
+public final class CoreImplPlugin extends JavaPlugin {
@Override
public void onEnable() {
@@ -29,7 +30,7 @@ public class CoreImplPlugin extends JavaPlugin {
ScrowAPI.getCommandApi().register(new LobbyCommand().getRootLiteral());
}
- getServer().getPluginManager().registerEvents(new ChatStyleListener(), this);
+ getServer().getPluginManager().registerEvents(new ChatStyleListener(new NoPrefixPrefixCache()), this);
// TODO/FIXME
//registerServer();
diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java
index a3afa63..71c2a68 100644
--- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java
+++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java
@@ -15,7 +15,6 @@ import de.kentoj.scrowlib.instancemanager.InstanceManagerImpl;
import de.kentoj.scrowlib.messaging.InMemoyMessageBroker;
import de.kentoj.scrowlib.messaging.NatsMessageBroker;
import de.kentoj.scrowlib.player.NetworkPlayerImpl;
-import de.kentoj.scrowlib.utils.EnvUtils;
import io.nats.client.Nats;
import io.nats.client.Options;
import lombok.AccessLevel;
@@ -35,7 +34,7 @@ public class ScrowAPISurface {
ScrowAPI.setCorePlugin(plugin);
var natsHost = System.getenv("HOST_NATS");
- if (natsHost!= null) {
+ if (natsHost != null) {
try {
var options = Options.builder()
.server(natsHost)
@@ -56,7 +55,7 @@ public class ScrowAPISurface {
Class.forName("org.postgresql.Driver", true, ScrowAPISurface.class.getClassLoader());
var props = new Properties();
props.setProperty("user", "postgres");
- ScrowAPI.setJdbi(Jdbi.create(hostPostgres));
+ ScrowAPI.setJdbi(Jdbi.create(hostPostgres, props));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/ChatStyleListener.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/ChatStyleListener.java
new file mode 100644
index 0000000..3e58b30
--- /dev/null
+++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/ChatStyleListener.java
@@ -0,0 +1,32 @@
+package de.kentoj.scrow.bukkit.chatstyle;
+
+import de.kentoj.scrowlib.utils.ComponentUtils;
+import io.papermc.paper.event.player.AsyncChatEvent;
+import lombok.RequiredArgsConstructor;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+@RequiredArgsConstructor
+public class ChatStyleListener implements Listener {
+
+ private final PrefixCache prefixCache;
+
+ @EventHandler
+ public void onChat(AsyncChatEvent ev) {
+ Component formattedMessage = formatMessage(ev.getPlayer(), ev.message());
+
+ ev.renderer((source, sourceDisplayName, message, viewer) -> formattedMessage);
+ }
+
+ private Component formatMessage(Player player, Component message) {
+ var prefix = prefixCache.getPrefix(player.getUniqueId());
+ Component msg = prefix != null
+ ? prefix.append(Component.text("│").color(NamedTextColor.DARK_GRAY))
+ : Component.empty();
+ return msg.append(Component.text(player.getName() + ": ").color(NamedTextColor.GRAY))
+ .append(ComponentUtils.resolveURLS(message).colorIfAbsent(NamedTextColor.GRAY));
+ }
+}
diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/LuckPermsPrefixCache.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/LuckPermsPrefixCache.java
new file mode 100644
index 0000000..80953f5
--- /dev/null
+++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/LuckPermsPrefixCache.java
@@ -0,0 +1,36 @@
+package de.kentoj.scrow.bukkit.chatstyle;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+import net.luckperms.api.LuckPerms;
+import net.luckperms.api.event.user.UserDataRecalculateEvent;
+import org.bukkit.Bukkit;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class LuckPermsPrefixCache implements PrefixCache {
+ private final Map cache = new HashMap<>();
+
+ // TODO load on join, unload on quit
+ public LuckPermsPrefixCache(LuckPerms luckPerms) {
+ //noinspection resource
+ luckPerms.getEventBus().subscribe(UserDataRecalculateEvent.class, this::onDataChange);
+ }
+
+ private void onDataChange(UserDataRecalculateEvent ev) {
+ var playerId = ev.getUser().getUniqueId();
+ if (Bukkit.getPlayer(playerId) == null) return;
+
+ var prefix = ev.getUser().getCachedData().getMetaData().getPrefix();
+ var prefixComponent = PlainTextComponentSerializer.plainText().deserialize(prefix);
+ cache.put(playerId, prefixComponent);
+ }
+
+ @Override
+ public @Nullable Component getPrefix(UUID playerId) {
+ return cache.get(playerId);
+ }
+}
diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/NoPrefixPrefixCache.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/NoPrefixPrefixCache.java
new file mode 100644
index 0000000..cf6a460
--- /dev/null
+++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/NoPrefixPrefixCache.java
@@ -0,0 +1,13 @@
+package de.kentoj.scrow.bukkit.chatstyle;
+
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+
+public class NoPrefixPrefixCache implements PrefixCache {
+ @Override
+ public @Nullable Component getPrefix(UUID playerId) {
+ return null;
+ }
+}
diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/PrefixCache.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/PrefixCache.java
new file mode 100644
index 0000000..48575bf
--- /dev/null
+++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/chatstyle/PrefixCache.java
@@ -0,0 +1,11 @@
+package de.kentoj.scrow.bukkit.chatstyle;
+
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+
+public interface PrefixCache {
+
+ @Nullable Component getPrefix(UUID playerId);
+}
diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/listener/ChatStyleListener.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/listener/ChatStyleListener.java
deleted file mode 100644
index 7cb2ab2..0000000
--- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/listener/ChatStyleListener.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package de.kentoj.scrow.bukkit.listener;
-
-import io.papermc.paper.event.player.AsyncChatEvent;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.format.NamedTextColor;
-import org.bukkit.entity.Player;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-
-public class ChatStyleListener implements Listener {
-
- @EventHandler
- public void onChat(AsyncChatEvent event) {
- Player player = event.getPlayer();
-
- Component formattedMessage = formatMessage(player, event.message());
-
- event.renderer((source, sourceDisplayName, message, viewer) -> formattedMessage);
- }
-
- private Component formatMessage(Player player, Component message) {
- return Component.text()
- .append(player.displayName()
- .append(Component.text(": "))
- .color(NamedTextColor.GRAY))
- .append(message.color(NamedTextColor.WHITE))
- .build();
- }
-}
diff --git a/core-lib/build.gradle.kts b/core-lib/build.gradle.kts
index 73fbe7d..ed695dc 100644
--- a/core-lib/build.gradle.kts
+++ b/core-lib/build.gradle.kts
@@ -13,7 +13,9 @@ dependencies {
compileOnly(libs.adventure.api)
compileOnly(libs.adventure.minimessage)
api(libs.bson)
- api(libs.scrow.commandapi.core)
+ api(libs.scrow.commandapi.core) {
+ this.isChanging = true
+ }
}
publishing {
diff --git a/core-lib/src/main/java/de/kentoj/scrowlib/utils/ComponentUtils.java b/core-lib/src/main/java/de/kentoj/scrowlib/utils/ComponentUtils.java
new file mode 100644
index 0000000..5f9ef03
--- /dev/null
+++ b/core-lib/src/main/java/de/kentoj/scrowlib/utils/ComponentUtils.java
@@ -0,0 +1,23 @@
+package de.kentoj.scrowlib.utils;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextReplacementConfig;
+import net.kyori.adventure.text.event.ClickEvent;
+
+import java.util.regex.Pattern;
+
+@NoArgsConstructor(access = AccessLevel.NONE)
+public class ComponentUtils {
+ private static final Pattern URL_REGEX = Pattern.compile("(https?://)?[a-z0-9]+(\\.[a-z0-9]+)*(\\.[a-z0-9]{1,10})((/+)[^/ ]*)*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
+ private static final TextReplacementConfig REPLACER = TextReplacementConfig.builder()
+ .match(URL_REGEX)
+ .replacement((c) -> c.clickEvent(ClickEvent.openUrl(c.content().startsWith("http") ? c.content() : "https://" + c.content())))
+ .build();
+
+
+ public static Component resolveURLS(Component component) {
+ return component.replaceText(REPLACER);
+ }
+}
diff --git a/core-velocity-api/build.gradle.kts b/core-velocity-api/build.gradle.kts
index f14fdac..3846896 100644
--- a/core-velocity-api/build.gradle.kts
+++ b/core-velocity-api/build.gradle.kts
@@ -12,7 +12,9 @@ dependencies {
api(project(":core-lib"))
api(libs.velocity.api)
- api(libs.scrow.commandapi.velocity)
+ api(libs.scrow.commandapi.velocity) {
+ isChanging = true
+ }
}
publishing {
diff --git a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java
index d3e4f33..0c1a24b 100644
--- a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java
+++ b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java
@@ -1,47 +1,53 @@
package de.kentoj.scrow.corevelocity;
import com.google.inject.Inject;
+import com.velocitypowered.api.event.Subscribe;
+import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.ProxyServer;
-import de.kentoj.scrow.corevelocity.command.OnlineCommand;
+import de.kentoj.scrow.corevelocity.misc.OnlineCommand;
import de.kentoj.scrow.corevelocity.network.FriendNotifications;
+import de.kentoj.scrow.corevelocity.network.SendPlayerWatchdog;
+import de.kentoj.scrow.corevelocity.network.ServerRegisterWatchdog;
import de.kentoj.scrow.corevelocity.privmsg.LastTargetCache;
import de.kentoj.scrow.corevelocity.privmsg.MsgCommand;
import de.kentoj.scrow.corevelocity.privmsg.PrivMsgHandler;
import de.kentoj.scrow.corevelocity.privmsg.ReplyCommand;
-import de.kentoj.scrow.corevelocity.network.SendPlayerWatchdog;
-import de.kentoj.scrow.corevelocity.network.ServerRegisterWatchdog;
import de.kentoj.scrow.generated.BuildInfo;
import de.kentoj.scrow.velocity.ScrowAPI;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
-import lombok.extern.java.Log;
+import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.format.NamedTextColor;
+@Slf4j
@Plugin(
id = "corevelocity",
name = "CoreVelocity",
+ authors = {"gradlehater57"},
version = BuildInfo.VERSION // may not resolve until building for the first time
)
-@Log
-public class CoreVelocityPlugin {
+public final class CoreVelocityPlugin {
+
@Inject
- public CoreVelocityPlugin(ProxyServer server) {
+ private ProxyServer server;
+
+ @Subscribe
+ private void onInit(ProxyInitializeEvent __) {
ScrowAPISurface.initScrowAPI(server);
var cmds = ScrowAPI.getCommandApi();
- {
- var lastTargetCache = new LastTargetCache();
- var style = new ScrowMessageStyle("MSG", NamedTextColor.LIGHT_PURPLE);
- var privmsgHelper = new PrivMsgHandler(style);
- cmds.register(new ReplyCommand(server, lastTargetCache, privmsgHelper, style).getRootLiteral());
- cmds.register(new MsgCommand(server, lastTargetCache, privmsgHelper, style).getRootLiteral());
- cmds.register(new OnlineCommand(server).getRootLiteral());
- }
+ var lastTargetCache = new LastTargetCache();
+ var style = new ScrowMessageStyle("MSG", NamedTextColor.LIGHT_PURPLE);
+ var privmsgHelper = new PrivMsgHandler(style);
+ cmds.register(new ReplyCommand(server, lastTargetCache, privmsgHelper, style).getRootLiteral());
+ cmds.register(new MsgCommand(server, lastTargetCache, privmsgHelper, style).getRootLiteral());
+ cmds.register(new OnlineCommand(server).getRootLiteral());
var registerWatchdog = new ServerRegisterWatchdog(server);
var sendPlayerWatchdog = new SendPlayerWatchdog(server);
registerWatchdog.listen();
sendPlayerWatchdog.listen();
+
server.getEventManager().register(this, new FriendNotifications());
}
}
\ No newline at end of file
diff --git a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/ScrowAPISurface.java b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/ScrowAPISurface.java
index 293d029..85ec5b1 100644
--- a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/ScrowAPISurface.java
+++ b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/ScrowAPISurface.java
@@ -45,7 +45,7 @@ public class ScrowAPISurface {
Class.forName("org.postgresql.Driver", true, ScrowAPISurface.class.getClassLoader());
var props = new Properties();
props.setProperty("user", "postgres");
- ScrowAPI.setJdbi(Jdbi.create(hostPostgres));
+ ScrowAPI.setJdbi(Jdbi.create(hostPostgres, props));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
diff --git a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/command/OnlineCommand.java b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/misc/OnlineCommand.java
similarity index 96%
rename from core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/command/OnlineCommand.java
rename to core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/misc/OnlineCommand.java
index ac2acdc..379a7b1 100644
--- a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/command/OnlineCommand.java
+++ b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/misc/OnlineCommand.java
@@ -1,4 +1,4 @@
-package de.kentoj.scrow.corevelocity.command;
+package de.kentoj.scrow.corevelocity.misc;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
diff --git a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/PrivMsgHandler.java b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/PrivMsgHandler.java
index 4b053cf..d431722 100644
--- a/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/PrivMsgHandler.java
+++ b/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/PrivMsgHandler.java
@@ -13,7 +13,7 @@ public class PrivMsgHandler {
private final ScrowMessageStyle style;
public void handle(Player player, Player target, String msg) {
- target.sendMessage(style.ok(text(player.getUsername() + " -> You: ").append(Component.text(msg))));
- player.sendMessage(style.ok(text("You -> " + player.getUsername() + ": ").append(Component.text(msg))));
+ target.sendMessage(style.ok(text(target.getUsername() + " -> You: ").append(Component.text(msg))));
+ player.sendMessage(style.ok(text("You -> " + target.getUsername() + ": ").append(Component.text(msg))));
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e893ca8..3c2344b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -8,6 +8,7 @@ papermc = "1.21.11-rc3-R0.1-SNAPSHOT"
velocity = "3.5.0-SNAPSHOT"
bson = "5.8.0"
adventure = "5.2.0"
+luckperms = "5.5"
[plugins]
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
@@ -29,6 +30,8 @@ papermc-api = { module = "io.papermc.paper:paper-api", version.ref = "papermc" }
velocity-api = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }
adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure" }
adventure-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
+# https://luckperms.net/wiki/Developer-API
+luckperms = { module = "net.luckperms:api", version.ref = "luckperms" }
# http://forge.kentoj.de/scrow/-/packages/maven/de.kentoj.scrow:kencommandapi-core
scrow-commandapi-core = { module = "de.kentoj.scrow:kencommandapi-core", version.ref = "scrow-commandapi" }