Add Paper platform (#541)

* Add paper platform

* Fix package name for Paper module

* Load adventure-nbt library on paper

* Add missing license header

---------

Co-authored-by: Jones <admin@jonesdev.xyz>
This commit is contained in:
JohnandJones authored and GitHub committed 2026-01-05 15:04:59 +01:00
1 parent 2a98d4bdb0
commit 878789b2c1
9 files changed
+301 -21

No files matched your search

@@ -30,6 +30,8 @@ public enum SonarPlatform {
pipeline -> pipeline.context("outbound_config") != null ? "outbound_config" : "encoder"), pipeline -> pipeline.context("outbound_config") != null ? "outbound_config" : "encoder"),
BUNGEE("BungeeCord", 19109, "inbound-boss", BUNGEE("BungeeCord", 19109, "inbound-boss",
pipeline -> "packet-encoder"), pipeline -> "packet-encoder"),
PAPER("Paper", 19110, "packet_handler",
pipeline -> pipeline.context("outbound_config") != null ? "outbound_config" : "encoder"),
VELOCITY("Velocity", 19107, "handler", VELOCITY("Velocity", 19107, "handler",
pipeline -> "minecraft-encoder"); pipeline -> "minecraft-encoder");
+1 -1
View File
@@ -102,7 +102,7 @@ allprojects {
tasks { tasks {
// This is a small wrapper tasks to simplify the building process // This is a small wrapper tasks to simplify the building process
register("build-sonar") { register("build-sonar") {
val subprojects = listOf("api", "captcha", "common", "bukkit", "bungeecord", "velocity") val subprojects = listOf("api", "captcha", "common", "bukkit", "bungeecord", "paper", "velocity")
val buildTasks = subprojects.flatMap { listOf("$it:clean", "$it:spotlessApply", "$it:shadowJar") } val buildTasks = subprojects.flatMap { listOf("$it:clean", "$it:spotlessApply", "$it:shadowJar") }
dependsOn(buildTasks) dependsOn(buildTasks)
} }
@@ -68,26 +68,38 @@ class LibraryLoader {
// Only load adventure if not on Velocity // Only load adventure if not on Velocity
if (platform != SonarPlatform.VELOCITY) { if (platform != SonarPlatform.VELOCITY) {
libraryManager.loadLibraries( if (platform != SonarPlatform.PAPER) {
Library.builder() libraryManager.loadLibraries(
.groupId("net{}kyori") Library.builder()
.artifactId("adventure-text-minimessage") .groupId("net{}kyori")
.version("4.21.0") .artifactId("adventure-text-minimessage")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori") .version("4.21.0")
.build(), .relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
Library.builder() .build(),
.groupId("net{}kyori") Library.builder()
.artifactId("adventure-text-serializer-gson") .groupId("net{}kyori")
.version("4.21.0") .artifactId("adventure-text-serializer-gson")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori") .version("4.21.0")
.build(), .relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
Library.builder() .build(),
.groupId("net{}kyori") Library.builder()
.artifactId("adventure-nbt") .groupId("net{}kyori")
.version("4.21.0") .artifactId("adventure-nbt")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori") .version("4.21.0")
.build() .relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
); .build()
);
} else {
// On Paper, we only relocate the nbt package since the rest of adventure is bundled with the server.
libraryManager.loadLibrary(
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-nbt")
.version("4.21.0")
.relocate("net{}kyori{}adventure{}nbt", "xyz{}jonesdev{}sonar{}libs{}kyori{}adventure{}nbt")
.build()
);
}
} }
} }
} }
+3
View File
@@ -12,6 +12,7 @@ shadow = "com.gradleup.shadow:8.3.8"
spotless = "com.diffplug.spotless:7.2.1" spotless = "com.diffplug.spotless:7.2.1"
pluginyml-bungee = { id = "net.minecrell.plugin-yml.bungee", version.ref = "pluginyml" } pluginyml-bungee = { id = "net.minecrell.plugin-yml.bungee", version.ref = "pluginyml" }
pluginyml-bukkit = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" } pluginyml-bukkit = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
pluginyml-paper = { id = "net.minecrell.plugin-yml.paper", version.ref = "pluginyml" }
[libraries] [libraries]
netty = { module = "io.netty:netty-all", version.ref = "netty" } netty = { module = "io.netty:netty-all", version.ref = "netty" }
@@ -23,6 +24,7 @@ adventure-platform-bungee = { module = "net.kyori:adventure-platform-bungeecord"
libby-core = { module = "com.alessiodp.libby:libby-core", version.ref = "libby" } libby-core = { module = "com.alessiodp.libby:libby-core", version.ref = "libby" }
libby-bungee = { module = "com.alessiodp.libby:libby-bungee", version.ref = "libby" } libby-bungee = { module = "com.alessiodp.libby:libby-bungee", version.ref = "libby" }
libby-bukkit = { module = "com.alessiodp.libby:libby-bukkit", version.ref = "libby" } libby-bukkit = { module = "com.alessiodp.libby:libby-bukkit", version.ref = "libby" }
libby-paper = { module = "com.alessiodp.libby:libby-paper", version.ref = "libby" }
libby-velocity = { module = "com.alessiodp.libby:libby-velocity", version.ref = "libby" } libby-velocity = { module = "com.alessiodp.libby:libby-velocity", version.ref = "libby" }
bstats-bungee = { module = "org.bstats:bstats-bungeecord", version.ref = "bstats" } bstats-bungee = { module = "org.bstats:bstats-bungeecord", version.ref = "bstats" }
bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" } bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" }
@@ -35,5 +37,6 @@ ormlite = "com.j256.ormlite:ormlite-jdbc:6.1"
lombok = "org.projectlombok:lombok:1.18.42" lombok = "org.projectlombok:lombok:1.18.42"
bungeecord = "net.md-5:bungeecord-proxy:1.21-SNAPSHOT" bungeecord = "net.md-5:bungeecord-proxy:1.21-SNAPSHOT"
velocity = "io.papermc.velocity:velocity-proxy:3.4.0-SNAPSHOT" velocity = "io.papermc.velocity:velocity-proxy:3.4.0-SNAPSHOT"
paper = "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT"
# We have to use 1.16.5 for backwards compatibility # We have to use 1.16.5 for backwards compatibility
spigot = "org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT" spigot = "org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT"
+52
View File
@@ -0,0 +1,52 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
plugins {
alias(libs.plugins.pluginyml.paper) apply true
}
paper {
name = rootProject.name
main = "xyz.jonesdev.sonar.paper.SonarPaperPlugin"
authors = listOf("Jones Development", "Sonar Contributors")
website = "https://jonesdev.xyz/discord/"
load = BukkitPluginDescription.PluginLoadOrder.POSTWORLD
apiVersion = "1.21" // ignore legacy plugin warning
foliaSupported = true
serverDependencies {
listOf("Geyser-Spigot", "floodgate", "Protocolize", "ProtocolSupport",
"ViaVersion", "packetevents", "ProtocolLib", "FastLogin").forEach {
register(it) {
required = false
}
}
}
}
java {
// Modern Paper requires Java 21
java.sourceCompatibility = JavaVersion.VERSION_21
java.targetCompatibility = JavaVersion.VERSION_21
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
repositories {
maven(url = "https://repo.papermc.io/repository/maven-public/") // Paper
}
dependencies {
implementation(project(":api"))
implementation(project(":common"))
implementation(project(":bukkit"))
compileOnly(rootProject.libs.paper)
implementation(rootProject.libs.bstats.bukkit)
implementation(rootProject.libs.libby.paper)
}
tasks {
shadowJar {
relocate("net.kyori.adventure.nbt", "xyz.jonesdev.sonar.libs.kyori.adventure.nbt")
}
}
@@ -0,0 +1,117 @@
/*
* Copyright (C) 2026 Sonar Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package xyz.jonesdev.sonar.paper;
import com.alessiodp.libby.PaperLibraryManager;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import lombok.Getter;
import net.kyori.adventure.audience.Audience;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.jonesdev.sonar.paper.command.PaperSonarCommand;
import xyz.jonesdev.sonar.api.SonarPlatform;
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
import xyz.jonesdev.sonar.bukkit.SonarBukkit;
import xyz.jonesdev.sonar.bukkit.antibot.BukkitInjector;
import xyz.jonesdev.sonar.bukkit.listener.BukkitJoinListener;
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;
import java.util.UUID;
@Getter
public final class SonarPaper extends SonarBootstrap<SonarPaperPlugin> {
private final LoggerWrapper logger = new LoggerWrapper() {
@Override
public void info(final String message, final Object... args) {
getPlugin().getLogger().info(buildFullMessage(message, args));
}
@Override
public void warn(final String message, final Object... args) {
getPlugin().getLogger().warning(buildFullMessage(message, args));
}
@Override
public void error(final String message, final Object... args) {
getPlugin().getLogger().severe(buildFullMessage(message, args));
}
};
private Metrics metrics;
public SonarPaper(final @NotNull SonarPaperPlugin plugin) {
super(plugin, SonarPlatform.PAPER, plugin.getDataFolder(), new PaperLibraryManager(plugin));
}
@Override
public @Nullable Audience audience(@Nullable final UUID uniqueId) {
if (uniqueId == null) return null;
return Bukkit.getPlayer(uniqueId);
}
@Override
public @NotNull Audience sender(@NotNull final Object object) {
return (Audience) object;
}
@Override
public @NotNull LoggerWrapper getLogger() {
return logger;
}
@Override
public void enable() {
// Initialize bStats.org metrics
metrics = new Metrics(getPlugin(), getPlatform().getMetricsId());
// Add charts for some configuration options
metrics.addCustomChart(new SimplePie("verification",
() -> getConfig().getVerification().getTiming().getDisplayName()));
metrics.addCustomChart(new SimplePie("captcha",
() -> getConfig().getVerification().getMap().getTiming().getDisplayName()));
metrics.addCustomChart(new SimplePie("language",
() -> getConfig().getLanguage().getName()));
metrics.addCustomChart(new SimplePie("database_type",
() -> getConfig().getDatabase().getType().getDisplayName()));
// Register Sonar command
getPlugin().getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event ->
event.registrar().register("sonar", new PaperSonarCommand()));
// Try to inject into the server
if (BukkitInjector.isLateBindEnabled()) {
getPlugin().getServer().getScheduler().runTask(getPlugin(), BukkitInjector::inject);
} else {
getPlugin().getServer().getPluginManager().registerEvents(new BukkitJoinListener(), getPlugin());
}
// Let the injector know that the plugin has been enabled
SonarBukkit.INITIALIZE_LISTENER.complete(null);
}
@Override
public void disable() {
// Make sure to properly stop the metrics
if (metrics != null) {
metrics.shutdown();
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2026 Sonar Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package xyz.jonesdev.sonar.paper;
import org.bukkit.plugin.java.JavaPlugin;
import xyz.jonesdev.sonar.bukkit.antibot.BukkitInjector;
public final class SonarPaperPlugin extends JavaPlugin {
private SonarPaper bootstrap;
@Override
public void onLoad() {
// Inject early if late-bind is disabled
if (!BukkitInjector.isLateBindEnabled()) {
BukkitInjector.inject();
}
}
@Override
public void onEnable() {
bootstrap = new SonarPaper(this);
bootstrap.initialize();
}
@Override
public void onDisable() {
bootstrap.shutdown();
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2026 Sonar Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package xyz.jonesdev.sonar.paper.command;
import io.papermc.paper.command.brigadier.BasicCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jspecify.annotations.NonNull;
import xyz.jonesdev.sonar.api.command.InvocationSource;
import xyz.jonesdev.sonar.api.command.SonarCommand;
import java.util.Collection;
import java.util.Collections;
public final class PaperSonarCommand implements BasicCommand, SonarCommand {
@Override
public void execute(final CommandSourceStack commandSourceStack, final String @NonNull [] args) {
final CommandSender sender = commandSourceStack.getSender();
final InvocationSource invocationSource = new InvocationSource(
sender instanceof Player ? ((Player) sender).getUniqueId() : null,
sender,
sender::hasPermission
);
handle(invocationSource, args);
}
@Override
public @NonNull Collection<String> suggest(final CommandSourceStack commandSourceStack, final String @NonNull [] args) {
// Do not allow tab completion if the player does not have the required permission
return commandSourceStack.getSender().hasPermission("sonar.command") ?
getCachedTabSuggestions(args) : Collections.emptyList();
}
}
+1
View File
@@ -6,6 +6,7 @@ sequenceOf(
"common", "common",
"bukkit", "bukkit",
"bungeecord", "bungeecord",
"paper",
"velocity" "velocity"
).forEach { ).forEach {
include(":$it") include(":$it")