Compare commits

..
7 Commits
Author SHA1 Message Date
Jones 0fd7f0c173 Release 2.1.44 (#545) 2026-01-10 22:25:43 +01:00
Jones f197260426 Always skip EaglerCraft players 2026-01-10 22:10:15 +01:00
Squesly cbae477982 Fix connection has already been closed (#544)
* Fixed a MySQL issue: connection has already been closed.

* Fixed import
2026-01-10 09:55:51 +01:00
JohnandJones 878789b2c1 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>
2026-01-05 15:04:59 +01:00
Jones 2a98d4bdb0 Remove mentions of nonexistent tests in dependencies 2026-01-01 13:37:17 +01:00
Jones 790d233870 Make LibraryLoader use correct MiniMessage versions 2026-01-01 13:10:54 +01:00
Jones e732da6af2 [skip ci] Update sponsors 2025-12-27 00:42:30 +01:00
18 changed files with 344 additions and 32 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
patreon: jonesdev
+2 -3
View File
@@ -55,12 +55,11 @@ Sonar is unlikely to ever falsely prevent a player from joining the server since
If you or one of your players experiences a false positive, make sure to report it by opening a [GitHub issue](https://github.com/jonesdevelopment/sonar/issues/new/choose) or a ticket on the [Discord server](https://jonesdev.xyz/discord/).
## Sponsors
Massive thanks to the sponsors of Sonar who help keep this project running:
<a href="https://github.com/Chumb3x"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/79834154?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
If you are interested in sponsoring this project, please contact me via [Discord](https://jonesdev.xyz/discord/).
### Past Sponsors
<a href="https://github.com/Chumb3x"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/79834154?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
<a href="https://github.com/Hydoxl"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/107579333?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
<a href="https://github.com/ItzErpandX"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/84748484?v=4?s=400?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
@@ -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");
@@ -20,7 +20,7 @@ package xyz.jonesdev.sonar.api.database.controller;
import com.alessiodp.libby.LibraryManager;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.jdbc.JdbcConnectionSource;
import com.j256.ormlite.jdbc.JdbcPooledConnectionSource;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
@@ -93,7 +93,7 @@ public final class VerifiedPlayerController {
"?autoReconnect=true");
}
connectionSource = new JdbcConnectionSource(jdbcURL,
connectionSource = new JdbcPooledConnectionSource(jdbcURL,
Sonar.get0().getConfig().getGeneralConfig().getString("database.username"),
Sonar.get0().getConfig().getGeneralConfig().getString("database.password"),
cachedDatabaseType.getDatabaseType());
+1 -1
View File
@@ -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)
}
-1
View File
@@ -32,7 +32,6 @@ dependencies {
implementation(project(":common"))
compileOnly(rootProject.libs.spigot)
testCompileOnly(rootProject.libs.spigot)
implementation(rootProject.libs.adventure.platform.bukkit) {
exclude(module = "adventure-nbt")
-1
View File
@@ -14,7 +14,6 @@ dependencies {
implementation(project(":common"))
compileOnly(rootProject.libs.bungeecord)
testCompileOnly(rootProject.libs.bungeecord)
implementation(rootProject.libs.adventure.platform.bungee) {
exclude(module = "adventure-nbt")
@@ -30,6 +30,7 @@ import xyz.jonesdev.sonar.common.protocol.SonarPacket;
import xyz.jonesdev.sonar.common.protocol.SonarPacketEncoder;
import xyz.jonesdev.sonar.common.protocol.SonarPacketRegistry;
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
import xyz.jonesdev.sonar.common.util.EaglerUtil;
import xyz.jonesdev.sonar.common.util.GeyserUtil;
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
@@ -120,7 +121,8 @@ public abstract class InboundHandlerAdapter extends ChannelInboundHandlerAdapter
}
// Don't continue the verification process if the verification is disabled
if (!Sonar.get0().getAntiBot().shouldVerifyNewPlayers()) {
if (!Sonar.get0().getAntiBot().shouldVerifyNewPlayers()
|| EaglerUtil.isEaglerConnection(ctx.channel())) {
initialLogin(ctx.channel(), inetAddress, initialLoginAction);
return;
}
@@ -68,26 +68,38 @@ class LibraryLoader {
// Only load adventure if not on Velocity
if (platform != SonarPlatform.VELOCITY) {
libraryManager.loadLibraries(
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-minimessage")
.version("4.13.1") // adventure-platforms hasn't updated in years... Please update! :'(
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-serializer-gson")
.version("4.18.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-nbt")
.version("4.18.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()
);
}
}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2025 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.common.util;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
@UtilityClass
public class EaglerUtil {
// https://github.com/lax1dude/eaglerxserver/blob/main/core/src/main/java/net/lax1dude/eaglercraft/backend/server/adapter/PipelineAttributes.java
public static final AttributeKey<Object> EAGLER_LISTENER_DATA = AttributeKey.valueOf("$eagler0");
public static final AttributeKey<Object> EAGLER_PIPELINE_DATA = AttributeKey.valueOf("$eagler1");
public boolean isEaglerConnection(final @NotNull Channel channel) {
return channel.attr(EAGLER_LISTENER_DATA).get() != null
|| channel.attr(EAGLER_PIPELINE_DATA).get() != null;
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
group=xyz.jonesdev
version=2.1.43
version=2.1.44
description=A lightweight, effective, and easy-to-use anti-bot plugin for Velocity, BungeeCord, and Bukkit.
+3
View File
@@ -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" }
@@ -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" }
@@ -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
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",
"bukkit",
"bungeecord",
"paper",
"velocity"
).forEach {
include(":$it")
-1
View File
@@ -3,7 +3,6 @@ dependencies {
implementation(project(":common"))
compileOnly(rootProject.libs.velocity)
testCompileOnly(rootProject.libs.velocity)
implementation(rootProject.libs.bstats.velocity)
implementation(rootProject.libs.libby.velocity)