Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/main/java/xyz/jonesdev/sonar/api/SonarPlatform.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public enum SonarPlatform {
pipeline -> pipeline.context("outbound_config") != null ? "outbound_config" : "encoder"),
BUNGEE("BungeeCord", 19109, "inbound-boss",
pipeline -> "packet-encoder"),
PAPER("Paper", 19110, "packet_handler",
pipeline -> pipeline.context("outbound_config") != null ? "outbound_config" : "encoder"),
VELOCITY("Velocity", 19107, "handler",
pipeline -> "minecraft-encoder");

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ allprojects {
tasks {
// This is a small wrapper tasks to simplify the building process
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") }
dependsOn(buildTasks)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,26 +68,38 @@ void loadLibraries(final @NotNull LibraryManager libraryManager, final @NotNull

// Only load adventure if not on Velocity
if (platform != SonarPlatform.VELOCITY) {
libraryManager.loadLibraries(
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-minimessage")
.version("4.21.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-serializer-gson")
.version("4.21.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-nbt")
.version("4.21.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build()
);
if (platform != SonarPlatform.PAPER) {
libraryManager.loadLibraries(
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-minimessage")
.version("4.21.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-serializer-gson")
.version("4.21.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-nbt")
.version("4.21.0")
.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 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ shadow = "com.gradleup.shadow:8.3.8"
spotless = "com.diffplug.spotless:7.2.1"
pluginyml-bungee = { id = "net.minecrell.plugin-yml.bungee", 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]
netty = { module = "io.netty:netty-all", version.ref = "netty" }
Expand All @@ -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-bungee = { module = "com.alessiodp.libby:libby-bungee", 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" }
bstats-bungee = { module = "org.bstats:bstats-bungeecord", version.ref = "bstats" }
bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" }
Expand All @@ -35,5 +37,6 @@ ormlite = "com.j256.ormlite:ormlite-jdbc:6.1"
lombok = "org.projectlombok:lombok:1.18.42"
bungeecord = "net.md-5:bungeecord-proxy:1.21-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
spigot = "org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT"
52 changes: 52 additions & 0 deletions paper/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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")
}
}
117 changes: 117 additions & 0 deletions paper/src/main/java/xyz/jonesdev/sonar/paper/SonarPaper.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
44 changes: 44 additions & 0 deletions paper/src/main/java/xyz/jonesdev/sonar/paper/SonarPaperPlugin.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ sequenceOf(
"common",
"bukkit",
"bungeecord",
"paper",
"velocity"
).forEach {
include(":$it")
Expand Down