Compare commits

..
Author SHA1 Message Date
Jones ef6602c2da Add sessionId field to LoginSuccessPacket 2026-06-19 15:14:50 +02:00
Jones 698edd543c Add onlineMode field to JoinGamePacket 2026-06-19 15:13:58 +02:00
Jones d1b0240d83 Implement 26.2 codec and tags 2026-06-19 15:06:06 +02:00
Jones b67477be87 Implement 26.2 protocol version 2026-06-19 15:03:27 +02:00
36 changed files with 131 additions and 396 deletions

No files matched your search

+1 -1
View File
@@ -47,7 +47,7 @@ Sonar analyzes a player's behavior before joining the actual server, therefore s
1. Sonar sends the player to a lightweight fake server when they connect for the first time.
2. Sonar verifies that players obey the laws of Minecraft's physics, including gravity and proper block collision.
3. Sonar verifies that players send legitimate packets when interacting with vehicles (e.g. boats).
4. Sonar makes sure that players send legitimate packets according to the [vanilla Minecraft protocol](<https://minecraft.wiki/w/Java_Edition_protocol/>).
4. Sonar makes sure that players send legitimate packets according to the [vanilla Minecraft protocol](<https://wiki.vg/Protocol>).
Sonar also protects against spambot attacks since it queues the incoming connections, therefore making it technically impossible to have a ton of bots join the server at the same time.
@@ -77,8 +77,7 @@ public enum ProtocolVersion {
MINECRAFT_1_21_9(773, "1.21.9"),
MINECRAFT_1_21_11(774, "1.21.11"),
MINECRAFT_26_1(775, "26.1"),
MINECRAFT_26_2(776, "26.2"),
MINECRAFT_26_3(777, "26.3");
MINECRAFT_26_2(776, "26.2");
private final int protocol;
private final String name;
+3 -6
View File
@@ -75,12 +75,6 @@ allprojects {
compileJava {
options.encoding = "UTF-8"
options.release.set(17)
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
disableAutoTargetJvm()
}
jar {
@@ -99,6 +93,9 @@ allprojects {
attributes["Git-Commit"] = gitCommit
}
}
java.sourceCompatibility = JavaVersion.VERSION_11
java.targetCompatibility = JavaVersion.VERSION_11
}
}
@@ -82,9 +82,7 @@ public enum BukkitServerVersion {
MINECRAFT_1_21_9,
MINECRAFT_1_21_10,
MINECRAFT_1_21_11,
MINECRAFT_26_1,
MINECRAFT_26_2,
MINECRAFT_26_3;
MINECRAFT_26_1;
static final BukkitServerVersion[] REVERSED_VALUES;
+3
View File
@@ -15,6 +15,9 @@ dependencies {
compileOnly(rootProject.libs.bungeecord)
implementation(rootProject.libs.adventure.platform.bungee) {
exclude(module = "adventure-nbt")
}
implementation(rootProject.libs.bstats.bungee)
implementation(rootProject.libs.libby.bungee)
}
@@ -1,92 +0,0 @@
/*
* 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.bungee;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.title.Title;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.chat.ComponentSerializer;
import org.jetbrains.annotations.NotNull;
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
import java.time.Duration;
/**
* Minimal {@link Audience} implementation backed by the native BungeeCord chat API.
* <br>
* BungeeCord does not ship Adventure, so we used to bundle adventure-platform-bungeecord (Adventure 4.x).
* That conflicted with the Adventure 5.x libraries Sonar loads at runtime, because both end up
* in the same relocated package. Converting components to JSON and passing them to BungeeCord's own
* {@link ComponentSerializer} allows us to depend on exactly one Adventure version.
*/
final class BungeeAudience implements Audience {
private final CommandSender sender;
BungeeAudience(final @NotNull CommandSender sender) {
this.sender = sender;
}
@Override
public void sendMessage(final @NotNull Component message) {
sender.sendMessage(toBungee(message));
}
@Override
public void sendActionBar(final @NotNull Component message) {
// Only players have an action bar
if (sender instanceof ProxiedPlayer player) {
player.sendMessage(ChatMessageType.ACTION_BAR, toBungee(message));
}
}
@Override
public void showTitle(final @NotNull Title title) {
// Only players can see titles
if (sender instanceof ProxiedPlayer player) {
final Title.Times times = title.times();
ProxyServer.getInstance().createTitle()
.title(toBungee(title.title()))
.subTitle(toBungee(title.subtitle()))
// Use the same defaults as Title#DEFAULT_TIMES (0.5s, 3.5s, 1s)
.fadeIn(times == null ? 10 : toTicks(times.fadeIn()))
.stay(times == null ? 70 : toTicks(times.stay()))
.fadeOut(times == null ? 20 : toTicks(times.fadeOut()))
.send(player);
}
}
private static int toTicks(final @NotNull Duration duration) {
return (int) (duration.toMillis() / 50L);
}
private @NotNull BaseComponent[] toBungee(final @NotNull Component component) {
// Downsample hex colors for clients that do not support them
final boolean legacyClient = sender instanceof ProxiedPlayer player
&& player.getPendingConnection().getVersion() < ProtocolVersion.MINECRAFT_1_16.getProtocol();
final GsonComponentSerializer serializer = legacyClient
? GsonComponentSerializer.colorDownsamplingGson()
: GsonComponentSerializer.gson();
return ComponentSerializer.parse(serializer.serialize(component));
}
}
@@ -20,8 +20,8 @@ package xyz.jonesdev.sonar.bungee;
import com.alessiodp.libby.BungeeLibraryManager;
import lombok.Getter;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import org.bstats.bungeecord.Metrics;
import org.bstats.charts.SimplePie;
import org.jetbrains.annotations.NotNull;
@@ -36,22 +36,26 @@ import java.util.UUID;
@Getter
public final class SonarBungee extends SonarBootstrap<SonarBungeePlugin> {
public SonarBungee(final @NotNull SonarBungeePlugin plugin, final @NotNull BungeeLibraryManager libraryManager) {
super(plugin, SonarPlatform.BUNGEE, plugin.getDataFolder(), libraryManager);
public SonarBungee(final @NotNull SonarBungeePlugin plugin) {
super(plugin, SonarPlatform.BUNGEE, plugin.getDataFolder(), new BungeeLibraryManager(plugin));
}
/**
* Wrapper for BungeeCord audiences
*/
private final BungeeAudiences bungeeAudiences = BungeeAudiences.create(getPlugin());
@Override
public @Nullable Audience audience(final @Nullable UUID uniqueId) {
if (uniqueId == null) {
return null;
}
final ProxiedPlayer player = getPlugin().getServer().getPlayer(uniqueId);
return player == null ? null : new BungeeAudience(player);
return bungeeAudiences.player(uniqueId);
}
@Override
public @NotNull Audience sender(final @NotNull Object object) {
return new BungeeAudience((CommandSender) object);
return bungeeAudiences.sender((CommandSender) object);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2026 Sonar Contributors
* 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
@@ -17,12 +17,9 @@
package xyz.jonesdev.sonar.bungee;
import com.alessiodp.libby.BungeeLibraryManager;
import lombok.Getter;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin;
import xyz.jonesdev.sonar.api.SonarPlatform;
import xyz.jonesdev.sonar.common.boot.LibraryLoader;
@Getter
public final class SonarBungeePlugin extends Plugin {
@@ -33,13 +30,7 @@ public final class SonarBungeePlugin extends Plugin {
public void onEnable() {
server = ProxyServer.getInstance();
// HACK:
// Adventure is not bundled in the jar, so the libraries have to be on the classpath
// before SonarBungee is loaded. The JVM resolves its Adventure types during class verification.
final BungeeLibraryManager libraryManager = new BungeeLibraryManager(this);
LibraryLoader.loadLibraries(libraryManager, SonarPlatform.BUNGEE);
bootstrap = new SonarBungee(this, libraryManager);
bootstrap = new SonarBungee(this);
bootstrap.initialize();
}
@@ -54,13 +54,13 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
static {
FONT = TTFFontProvider.loadFont("/assets/fonts/Kingthings_Trypewriter_2.ttf");
FBM.setAmount(0.55f + RANDOM.nextFloat(0.15f));
FBM.setScale(10 + RANDOM.nextFloat(10));
FBM.setAmount(0.6f);
FBM.setScale(15);
SMEAR.setShape(SmearFilter.SQUARES);
SMEAR.setDensity(0.1f);
SMEAR.setDistance(0);
SMEAR.setMix(0.25f + RANDOM.nextFloat(0.25f));
SMEAR.setMix(0.35f);
// Create fractions based on the number of colors
for (int i = 0; i < COLOR_FRACTIONS.length; i++) {
@@ -111,7 +111,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
private void applyRandomColorGradient(final @NotNull Graphics2D graphics) {
// Randomize the colors for the gradient effect
for (int i = 0; i < COLORS.length; i++) {
final float random = 0.8f + RANDOM.nextFloat() * 0.2f;
final float random = 0.9f + RANDOM.nextFloat() * 0.1f;
COLORS[i] = Color.getHSBColor(RANDOM.nextFloat(), random * 0.8f, random);
}
@@ -161,8 +161,8 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
}
private void createGlyphOutline(final @NotNull Graphics2D graphics, final @NotNull Shape shape) {
final float txy = RANDOM.nextFloat(0.1f, 2f);
final float width = RANDOM.nextFloat(0.5f, 2f);
final float txy = 1.25f + RANDOM.nextFloat();
final float width = 1 + RANDOM.nextFloat();
// Create a randomly translated and stroked shape based on the original glyph shape
final AffineTransform translation = AffineTransform.getTranslateInstance(txy, txy);
+2 -2
View File
@@ -17,7 +17,7 @@
import lombok.experimental.UtilityClass;
import xyz.jonesdev.sonar.api.antibot.captcha.CaptchaGenerator;
import xyz.jonesdev.sonar.captcha.StandardCaptchaGenerator;
import xyz.jonesdev.sonar.captcha.complex.ComplexCaptchaGenerator;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@@ -29,7 +29,7 @@ import java.util.Random;
public class Main {
public void main(final String... args) throws IOException {
final Random random = new Random();
final CaptchaGenerator standardCaptchaGenerator = new StandardCaptchaGenerator(null);
final CaptchaGenerator standardCaptchaGenerator = new ComplexCaptchaGenerator(null);
final long start = System.currentTimeMillis();
final char[] dictionary = {'a', 'b', 'c', 'd', 'e', 'f'/*, 'g'*/, 'h'/*, 'i'*/, 'j',
@@ -24,8 +24,8 @@ import org.jetbrains.annotations.NotNull;
import xyz.jonesdev.sonar.api.SonarPlatform;
@UtilityClass
public class LibraryLoader {
public void loadLibraries(final @NotNull LibraryManager libraryManager, final @NotNull SonarPlatform platform) {
class LibraryLoader {
void loadLibraries(final @NotNull LibraryManager libraryManager, final @NotNull SonarPlatform platform) {
libraryManager.addMavenCentral();
libraryManager.addJitPack();
libraryManager.loadLibraries(
@@ -69,14 +69,25 @@ public class LibraryLoader {
// Only load adventure if not on Velocity
if (platform != SonarPlatform.VELOCITY) {
if (platform != SonarPlatform.PAPER) {
// BungeeCord does not ship Adventure, and we do not bundle adventure-platform there anymore
// (mixing Adventure 4.x and 5.x in the same relocated package causes NoSuchMethodErrors).
// Therefore, adventure-api and everything else Adventure needs has to be resolved at runtime.
final boolean resolveTransitive = platform == SonarPlatform.BUNGEE;
libraryManager.loadLibraries(
adventureLibrary("adventure-text-minimessage", resolveTransitive),
adventureLibrary("adventure-text-serializer-gson", resolveTransitive),
adventureLibrary("adventure-nbt", resolveTransitive)
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.
@@ -84,25 +95,11 @@ public class LibraryLoader {
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-nbt")
.version("5.2.0")
.version("4.21.0")
.relocate("net{}kyori{}adventure{}nbt", "xyz{}jonesdev{}sonar{}libs{}kyori{}adventure{}nbt")
.build()
);
}
}
}
private @NotNull Library adventureLibrary(final @NotNull String artifactId, final boolean resolveTransitive) {
final Library.Builder builder = Library.builder()
.groupId("net{}kyori")
.artifactId(artifactId)
.version("5.2.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori");
if (resolveTransitive) {
builder.resolveTransitiveDependencies(true)
// Gson is already provided by the proxy, so we must not load another copy
.excludeTransitiveDependency("com.google.code.gson", "gson");
}
return builder.build();
}
}
@@ -68,13 +68,7 @@ public abstract class SonarBootstrap<T> implements Sonar {
final @NotNull File dataDirectory,
final @NotNull LibraryManager libraryManager) {
// Load all libraries before anything else
// HACK:
// We manually load the libraries on BungeeCord beforehand,
// so we skip this call to avoid loading libraries twice.
// See SonarBungeePlugin.java#L40
if (platform != SonarPlatform.BUNGEE) {
LibraryLoader.loadLibraries(libraryManager, platform);
}
LibraryLoader.loadLibraries(libraryManager, platform);
this.libraryManager = libraryManager;
// Check if Netty is up to date
ProtocolUtil.checkNettyVersion();
@@ -21,7 +21,6 @@ import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import org.jetbrains.annotations.NotNull;
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
@ChannelHandler.Sharable
public final class TailExceptionsHandler extends ChannelDuplexHandler {
@@ -34,20 +33,6 @@ public final class TailExceptionsHandler extends ChannelDuplexHandler {
// Additionally, this will also run after our custom decoder.
@Override
public void exceptionCaught(final @NotNull ChannelHandlerContext ctx, final Throwable cause) throws Exception {
// If ProtocolUtil#closeWith(...) already queued a graceful write-then-close for
// this channel (e.g. VerificationHandler#fail() calls disconnect() and then
// throws to unwind), don't race it with a second, immediate close() here. Doing
// so can abort the still-in-flight kick/disconnect packet write before it's fully
// flushed, so the client only receives a truncated packet instead of our message.
// The scheduled close from closeWith() will still run once the write completes.
// See: https://github.com/jonesdevelopment/sonar/issues/476
if (Boolean.TRUE.equals(ctx.channel().attr(ProtocolUtil.CLOSING).get())) {
if (LOG_EXCEPTIONS) {
cause.printStackTrace(System.err);
}
return;
}
// Close the channel if we encounter any errors.
ctx.close();
if (LOG_EXCEPTIONS) {
@@ -106,10 +106,8 @@ public class SonarPacketPreparer {
public final SonarPacket[] REGISTRY_SYNC_1_21_11 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_11);
public final SonarPacket[] REGISTRY_SYNC_26_1 = RegistryDataPacket.of(DimensionRegistry.CODEC_26_1);
public final SonarPacket[] REGISTRY_SYNC_26_2 = RegistryDataPacket.of(DimensionRegistry.CODEC_26_2);
public final SonarPacket[] REGISTRY_SYNC_26_3 = RegistryDataPacket.of(DimensionRegistry.CODEC_26_3);
public final SonarPacket TAGS_UPDATE_26_1 = UpdateTagsPacket.of(DimensionRegistry.TAGS_26_1);
public final SonarPacket TAGS_UPDATE_26_2 = UpdateTagsPacket.of(DimensionRegistry.TAGS_26_2);
public final SonarPacket TAGS_UPDATE_26_3 = UpdateTagsPacket.of(DimensionRegistry.TAGS_26_3);
public final SonarPacket START_WRITING_CHUNKS = new GameEventPacket(13, 0);
public final static SonarPacket INVALID_HELD_ITEM_SLOT = new SetHeldItemPacket(-1);
public final SonarPacket RANDOM_KEEP_ALIVE = new SonarPacketSnapshot(new KeepAlivePacket(RANDOM.nextInt()));
@@ -298,9 +296,7 @@ public class SonarPacketPreparer {
}
public static @Nullable SonarPacket getTagsPacket(final @NotNull ProtocolVersion protocolVersion) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
return TAGS_UPDATE_26_3;
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_2)) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_2)) {
return TAGS_UPDATE_26_2;
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_1)) {
return TAGS_UPDATE_26_1;
@@ -309,9 +305,7 @@ public class SonarPacketPreparer {
}
public static SonarPacket[] getRegistryPackets(final @NotNull ProtocolVersion protocolVersion) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
return REGISTRY_SYNC_26_3;
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_2)) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_2)) {
return REGISTRY_SYNC_26_2;
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_1)) {
return REGISTRY_SYNC_26_1;
@@ -77,8 +77,7 @@ public enum SonarPacketRegistry {
map(0x05, MINECRAFT_1_20_2, true),
map(0x07, MINECRAFT_1_20_5, true));
clientbound.register(UpdateTagsPacket.class, UpdateTagsPacket::new,
map(0x0D, MINECRAFT_1_20_5, true),
map(0x0E, MINECRAFT_26_3, true));
map(0x0D, MINECRAFT_1_20_5, true));
serverbound.register(ClientInformationPacket.class, ClientInformationPacket::new,
map(0x00, MINECRAFT_1_20_2, false));
@@ -114,8 +113,7 @@ public enum SonarPacketRegistry {
map(0x2C, MINECRAFT_1_21_2, true),
map(0x2B, MINECRAFT_1_21_5, true),
map(0x30, MINECRAFT_1_21_9, true),
map(0x31, MINECRAFT_26_1, true),
map(0x32, MINECRAFT_26_3, true));
map(0x31, MINECRAFT_26_1, true));
// minecraft:keep_alive
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
map(0x00, MINECRAFT_1_7_2, true),
@@ -135,8 +133,7 @@ public enum SonarPacketRegistry {
map(0x27, MINECRAFT_1_21_2, true),
map(0x26, MINECRAFT_1_21_5, true),
map(0x2B, MINECRAFT_1_21_9, true),
map(0x2C, MINECRAFT_26_1, true),
map(0x2D, MINECRAFT_26_3, true));
map(0x2C, MINECRAFT_26_1, true));
// minecraft:disconnect
clientbound.register(DisconnectPacket.class, DisconnectPacket::new,
map(0x40, MINECRAFT_1_7_2, true),
@@ -175,8 +172,7 @@ public enum SonarPacketRegistry {
map(0x42, MINECRAFT_1_21_2, true),
map(0x41, MINECRAFT_1_21_5, true),
map(0x46, MINECRAFT_1_21_9, true),
map(0x48, MINECRAFT_26_1, true),
map(0x49, MINECRAFT_26_3, true));
map(0x48, MINECRAFT_26_1, true));
// minecraft:player_abilities
clientbound.register(PlayerAbilitiesPacket.class, PlayerAbilitiesPacket::new,
map(0x39, MINECRAFT_1_7_2, true),
@@ -197,8 +193,7 @@ public enum SonarPacketRegistry {
map(0x3A, MINECRAFT_1_21_2, true),
map(0x39, MINECRAFT_1_21_5, true),
map(0x3E, MINECRAFT_1_21_9, true),
map(0x40, MINECRAFT_26_1, true),
map(0x41, MINECRAFT_26_3, true));
map(0x40, MINECRAFT_26_1, true));
// minecraft:level_chunk_with_light
clientbound.register(ChunkDataPacket.class, ChunkDataPacket::new,
map(0x21, MINECRAFT_1_7_2, true),
@@ -218,8 +213,7 @@ public enum SonarPacketRegistry {
map(0x28, MINECRAFT_1_21_2, true),
map(0x27, MINECRAFT_1_21_5, true),
map(0x2C, MINECRAFT_1_21_9, true),
map(0x2D, MINECRAFT_26_1, true),
map(0x2E, MINECRAFT_26_3, true));
map(0x2D, MINECRAFT_26_1, true));
// minecraft:remove_entities
clientbound.register(RemoveEntitiesPacket.class, RemoveEntitiesPacket::new,
map(0x13, MINECRAFT_1_7_2, true),
@@ -242,8 +236,7 @@ public enum SonarPacketRegistry {
map(0x47, MINECRAFT_1_21_2, true),
map(0x46, MINECRAFT_1_21_5, true),
map(0x4B, MINECRAFT_1_21_9, true),
map(0x4D, MINECRAFT_26_1, true),
map(0x4E, MINECRAFT_26_3, true));
map(0x4D, MINECRAFT_26_1, true));
// minecraft:set_passengers
clientbound.register(SetPassengersPacket.class, SetPassengersPacket::new,
map(0x1B, MINECRAFT_1_7_2, true),
@@ -263,8 +256,7 @@ public enum SonarPacketRegistry {
map(0x65, MINECRAFT_1_21_2, true),
map(0x64, MINECRAFT_1_21_5, true),
map(0x69, MINECRAFT_1_21_9, true),
map(0x6B, MINECRAFT_26_1, true),
map(0x6D, MINECRAFT_26_3, true));
map(0x6B, MINECRAFT_26_1, true));
// minecraft:add_entity
clientbound.register(SpawnEntityPacket.class, SpawnEntityPacket::new,
map(0x0E, MINECRAFT_1_7_2, true),
@@ -289,8 +281,7 @@ public enum SonarPacketRegistry {
map(0x4E, MINECRAFT_1_21_2, true),
map(0x4D, MINECRAFT_1_21_5, true),
map(0x52, MINECRAFT_1_21_9, true),
map(0x54, MINECRAFT_26_1, true),
map(0x56, MINECRAFT_26_3, true));
map(0x54, MINECRAFT_26_1, true));
// minecraft:ping
clientbound.register(TransactionPacket.class, TransactionPacket::new,
map(0x32, MINECRAFT_1_7_2, true),
@@ -309,8 +300,7 @@ public enum SonarPacketRegistry {
map(0x37, MINECRAFT_1_21_2, true),
map(0x36, MINECRAFT_1_21_5, true),
map(0x3B, MINECRAFT_1_21_9, true),
map(0x3D, MINECRAFT_26_1, true),
map(0x3E, MINECRAFT_26_3, true));
map(0x3D, MINECRAFT_26_1, true));
// minecraft:set_default_spawn_position
clientbound.register(SetDefaultSpawnPositionPacket.class, SetDefaultSpawnPositionPacket::new,
map(0x05, MINECRAFT_1_7_2, true),
@@ -332,8 +322,7 @@ public enum SonarPacketRegistry {
map(0x5B, MINECRAFT_1_21_2, true),
map(0x5A, MINECRAFT_1_21_5, true),
map(0x5F, MINECRAFT_1_21_9, true),
map(0x61, MINECRAFT_26_1, true),
map(0x63, MINECRAFT_26_3, true));
map(0x61, MINECRAFT_26_1, true));
// minecraft:map_item_data
clientbound.register(MapDataPacket.class, MapDataPacket::new,
map(0x34, MINECRAFT_1_7_2, true),
@@ -352,8 +341,7 @@ public enum SonarPacketRegistry {
map(0x2D, MINECRAFT_1_21_2, true),
map(0x2C, MINECRAFT_1_21_5, true),
map(0x31, MINECRAFT_1_21_9, true),
map(0x33, MINECRAFT_26_1, true),
map(0x34, MINECRAFT_26_3, true));
map(0x33, MINECRAFT_26_1, true));
// minecraft:container_set_slot
clientbound.register(SetContainerSlotPacket.class, SetContainerSlotPacket::new,
map(0x2F, MINECRAFT_1_7_2, true),
@@ -388,8 +376,7 @@ public enum SonarPacketRegistry {
map(0x61, MINECRAFT_1_21_2, true),
map(0x60, MINECRAFT_1_21_5, true),
map(0x65, MINECRAFT_1_21_9, true),
map(0x67, MINECRAFT_26_1, true),
map(0x69, MINECRAFT_26_3, true));
map(0x67, MINECRAFT_26_1, true));
// minecraft:system_chat
clientbound.register(SystemChatPacket.class, SystemChatPacket::new,
map(0x02, MINECRAFT_1_7_2, true),
@@ -408,23 +395,20 @@ public enum SonarPacketRegistry {
map(0x73, MINECRAFT_1_21_2, true),
map(0x72, MINECRAFT_1_21_5, true),
map(0x77, MINECRAFT_1_21_9, true),
map(0x79, MINECRAFT_26_1, true),
map(0x7C, MINECRAFT_26_3, true));
map(0x79, MINECRAFT_26_1, true));
// minecraft:game_event
clientbound.register(GameEventPacket.class, GameEventPacket::new,
map(0x20, MINECRAFT_1_20_3, true),
map(0x22, MINECRAFT_1_20_5, true),
map(0x23, MINECRAFT_1_21_2, true),
map(0x22, MINECRAFT_1_21_5, true),
map(0x26, MINECRAFT_1_21_9, true),
map(0x27, MINECRAFT_26_3, true));
map(0x26, MINECRAFT_1_21_9, true));
// minecraft:transfer
clientbound.register(TransferPacket.class, TransferPacket::new,
map(0x73, MINECRAFT_1_20_5, true),
map(0x7A, MINECRAFT_1_21_2, true),
map(0x7F, MINECRAFT_1_21_9, true),
map(0x81, MINECRAFT_26_1, true),
map(0x84, MINECRAFT_26_3, true));
map(0x81, MINECRAFT_26_1, true));
// minecraft:set_held_slot
clientbound.register(SetHeldItemPacket.class, SetHeldItemPacket::new,
map(0x09, MINECRAFT_1_7_2, true),
@@ -446,8 +430,7 @@ public enum SonarPacketRegistry {
map(0x63, MINECRAFT_1_21_2, true),
map(0x62, MINECRAFT_1_21_5, true),
map(0x67, MINECRAFT_1_21_9, true),
map(0x69, MINECRAFT_26_1, true),
map(0x6B, MINECRAFT_26_3, true));
map(0x69, MINECRAFT_26_1, true));
// minecraft:animate
clientbound.register(EntityAnimationPacket.class, EntityAnimationPacket::new,
map(0x0B, MINECRAFT_1_7_2, true),
@@ -479,8 +462,7 @@ public enum SonarPacketRegistry {
map(0x6B, MINECRAFT_1_21_2, true),
map(0x6A, MINECRAFT_1_21_5, true),
map(0x6F, MINECRAFT_1_21_9, true),
map(0x71, MINECRAFT_26_1, true),
map(0x73, MINECRAFT_26_3, true));
map(0x71, MINECRAFT_26_1, true));
// minecraft:client_tick_end
serverbound.register(ClientTickEndPacket.class, ClientTickEndPacket::new,
@@ -504,8 +486,7 @@ public enum SonarPacketRegistry {
map(0x31, MINECRAFT_1_21_2, false),
map(0x33, MINECRAFT_1_21_4, false),
map(0x34, MINECRAFT_1_21_6, false),
map(0x35, MINECRAFT_26_1, false),
map(0x36, MINECRAFT_26_3, false));
map(0x35, MINECRAFT_26_1, false));
// minecraft:paddle_boat
serverbound.register(PaddleBoatPacket.class, PaddleBoatPacket::new,
map(0x11, MINECRAFT_1_9, false),
@@ -544,7 +525,7 @@ public enum SonarPacketRegistry {
map(0x28, MINECRAFT_1_21_2, false),
map(0x29, MINECRAFT_1_21_4, false),
map(0x2A, MINECRAFT_1_21_6, false),
map(0x2B, MINECRAFT_26_1, false));
map(0x26, MINECRAFT_26_1, false));
// minecraft:move_vehicle
serverbound.register(VehicleMovePacket.class, VehicleMovePacket::new,
map(0x10, MINECRAFT_1_9, false),
@@ -753,8 +734,7 @@ public enum SonarPacketRegistry {
map(0x3A, MINECRAFT_1_21_4, false),
map(0x3B, MINECRAFT_1_21_5, false),
map(0x3C, MINECRAFT_1_21_6, false),
map(0x3F, MINECRAFT_26_1, false),
map(0x2E, MINECRAFT_26_3, false));
map(0x3F, MINECRAFT_26_1, false));
}
};
@@ -80,10 +80,7 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 9250;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 9451;
}
return 11185;
return 9451;
}, protocolVersion -> (double) 0.75f),
TRAPDOOR(protocolVersion -> {
// We have to use wooden trapdoors for 1.7 since 1.7 doesn't have iron trapdoors
@@ -138,10 +135,7 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 12380;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 12582;
}
return 14429;
return 12582;
}, protocolVersion -> 0.1875),
END_PORTAL_FRAME(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -189,10 +183,7 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 9272;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 9473;
}
return 11207;
return 9473;
}, protocolVersion -> 0.8125),
DAYLIGHT_SENSOR(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -243,10 +234,7 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 11093;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 11295;
}
return 13062;
return 11295;
}, protocolVersion -> 0.375),
COBBLESTONE_WALL(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -294,10 +282,7 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 9783;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 9984;
}
return 11726;
return 9984;
}, protocolVersion -> 1.5),
STONE_SLABS(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -348,10 +333,7 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 13197;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 13399;
}
return 15252;
return 13399;
}, protocolVersion -> 0.5),
WHITE_CARPET(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -402,10 +384,7 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 12694;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 12896;
}
return 14743;
return 12896;
}, protocolVersion -> protocolVersion.compareTo(MINECRAFT_1_8) < 0 ? 0 : 0.0625);
private final Function<ProtocolVersion, Integer> id;
@@ -43,10 +43,8 @@ public final class DimensionRegistry {
public final CompoundBinaryTag CODEC_1_21_11;
public final CompoundBinaryTag CODEC_26_1;
public final CompoundBinaryTag CODEC_26_2;
public final CompoundBinaryTag CODEC_26_3;
public final CompoundBinaryTag TAGS_26_1;
public final CompoundBinaryTag TAGS_26_2;
public final CompoundBinaryTag TAGS_26_3;
static {
CODEC_1_16 = getCodec("codec_1_16.nbt");
@@ -64,10 +62,8 @@ public final class DimensionRegistry {
CODEC_1_21_11 = getCodec("codec_1_21_11.nbt");
CODEC_26_1 = getCodec("codec_26_1.nbt");
CODEC_26_2 = getCodec("codec_26_2.nbt");
CODEC_26_3 = getCodec("codec_26_3.nbt");
TAGS_26_1 = getCodec("tags_26_1.nbt");
TAGS_26_2 = getCodec("tags_26_2.nbt");
TAGS_26_3 = getCodec("tags_26_3.nbt");
}
private @NotNull CompoundBinaryTag getCodec(final @NotNull String fileName) {
@@ -67,10 +67,7 @@ public enum EntityType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
return 87;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 89;
}
return 90;
return 89;
}),
MINECART(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
@@ -115,10 +112,7 @@ public enum EntityType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
return 84;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 85;
}
return 86;
return 85;
});
private final Function<ProtocolVersion, Integer> id;
@@ -85,13 +85,7 @@ public enum ItemType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 1104;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_1)) {
return 1105;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 1133;
}
return 1238;
return 1105;
}, protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
return 26;
@@ -105,10 +99,7 @@ public enum ItemType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 44;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 46;
}
return 48;
return 46;
});
private final Function<ProtocolVersion, Integer> id;
@@ -50,7 +50,7 @@ public final class RegistryDataPacket implements SonarPacket {
for (final RegistryDataPacket.Bundle bundle : bundles) {
ProtocolUtil.writeString(byteBuf, bundle.getName());
// Write the bundle tag
final BinaryTag tag = bundle.getTag();
final CompoundBinaryTag tag = bundle.getTag();
if (tag != null) {
byteBuf.writeBoolean(true);
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, tag);
@@ -73,7 +73,8 @@ public final class RegistryDataPacket implements SonarPacket {
final ArrayList<RegistryDataPacket.Bundle> bundles = new ArrayList<>();
for (final BinaryTag binaryTag : rootTag.getCompound(type).getList("value")) {
final CompoundBinaryTag tag = (CompoundBinaryTag) binaryTag;
bundles.add(new Bundle(tag.getString("name"), tag.get("element")));
// CompoundBinaryTag#getCompound(String) will return an empty compound tag when not exist.
bundles.add(new Bundle(tag.getString("name"), tag.get("element") == null ? null : tag.getCompound("element")));
}
packets[index++] = new SonarPacketSnapshot(new RegistryDataPacket(rootTag, type, bundles));
}
@@ -85,6 +86,6 @@ public final class RegistryDataPacket implements SonarPacket {
public static class Bundle {
@NotNull String name;
// In the protocol, it is able to be null
@Nullable BinaryTag tag;
@Nullable CompoundBinaryTag tag;
}
}
@@ -46,8 +46,6 @@ public final class AnimationPacket implements SonarPacket {
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
entityId = byteBuf.readInt();
type = LegacyAnimationType.getById(byteBuf.readByte());
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
hand = MAIN_HAND;
} else if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_8)) {
// Only 1.9+ clients have an offhand
hand = ProtocolUtil.readVarInt(byteBuf);
@@ -32,15 +32,6 @@ import xyz.jonesdev.sonar.common.util.ProtocolUtil;
@AllArgsConstructor
public final class ConfirmTeleportationPacket implements SonarPacket {
private int teleportId;
private double x;
private double y;
private double z;
private float yaw;
private float pitch;
public ConfirmTeleportationPacket(final int teleportId) {
this.teleportId = teleportId;
}
@Override
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
@@ -50,12 +41,5 @@ public final class ConfirmTeleportationPacket implements SonarPacket {
@Override
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
teleportId = ProtocolUtil.readVarInt(byteBuf);
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
x = byteBuf.readDouble();
y = byteBuf.readDouble();
z = byteBuf.readDouble();
yaw = byteBuf.readFloat();
pitch = byteBuf.readFloat();
}
}
}
@@ -148,14 +148,8 @@ public final class JoinGamePacket implements SonarPacket {
ProtocolUtil.writeString(byteBuf, levelName);
byteBuf.writeLong(partialHashedSeed);
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
ProtocolUtil.writeVarInt(byteBuf, gamemode);
// ByteBufCodecs#OPTIONAL_VAR_INT: 0 for empty, (value + 1) if present
ProtocolUtil.writeVarInt(byteBuf, previousGamemode == -1 ? 0 : previousGamemode + 1);
} else {
byteBuf.writeByte(gamemode);
byteBuf.writeByte(previousGamemode);
}
byteBuf.writeByte(gamemode);
byteBuf.writeByte(previousGamemode);
}
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16)) {
@@ -175,11 +169,12 @@ public final class JoinGamePacket implements SonarPacket {
ProtocolUtil.writeVarInt(byteBuf, seaLevel);
}
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
byteBuf.writeBoolean(secureProfile);
}
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_2)) {
byteBuf.writeBoolean(onlineMode);
byteBuf.writeBoolean(secureProfile);
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
byteBuf.writeBoolean(secureProfile);
}
}
@@ -24,7 +24,6 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import io.netty.util.AttributeKey;
import io.netty.util.Version;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.nbt.BinaryTag;
@@ -218,20 +217,9 @@ public class ProtocolUtil {
}
}
public static final AttributeKey<Boolean> CLOSING = AttributeKey.valueOf("sonar-graceful-close-in-progress");
public static void closeWith(final @NotNull Channel channel,
final @NotNull ProtocolVersion protocolVersion,
final @NotNull Object msg) {
// Mark this channel as already undergoing a graceful close *before* we queue the
// write. If something downstream (e.g. an exception thrown right after this call,
// such as VerificationHandler#fail()'s QuietDecoderException) reaches
// TailExceptionsHandler while our writeAndFlush(...).addListener(CLOSE) is still
// in flight, that handler must not call ctx.close() a second time - doing so can
// abort the still-pending write and deliver only a truncated packet to the client
// (which then sees a bare disconnect/reset instead of our kick message).
// See: https://github.com/jonesdevelopment/sonar/issues/476
channel.attr(CLOSING).set(true);
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
channel.eventLoop().execute(() -> {
channel.config().setAutoRead(false);
@@ -36,9 +36,8 @@ public final class GravityHandler extends VerificationHandler {
super(user);
this.preJoinHandler = preJoinHandler;
// Client can send movement packet without first position-and-rot packet (usually caused by teleport, < 26.3 Java)
// 26.3+ client will no longer send movement packet for teleports. (positions included in teleport confirm)
this.canFall = user.isGeyser() || user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3);
// Bedrock users start falling immediately
this.canFall = user.isGeyser();
// We don't want to check Geyser players for valid gravity, as this might cause issues because of the protocol
this.enableGravityCheck = !user.isGeyser() && AntiBot.shouldPerform(
Sonar.get0().getConfig().getVerification().getGravity().getTiming());
@@ -109,8 +108,6 @@ public final class GravityHandler extends VerificationHandler {
final SetPlayerPositionRotationPacket position = (SetPlayerPositionRotationPacket) packet;
if (teleported) {
handleMovement(position.getX(), position.getY(), position.getZ(), position.isOnGround(), true);
} else if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
return;
} else if (user.getProtocolVersion().equals(ProtocolVersion.MINECRAFT_1_21_2)) {
lastPositionPacket = position;
return;
@@ -125,8 +122,6 @@ public final class GravityHandler extends VerificationHandler {
if (teleported) {
final SetPlayerPositionPacket position = (SetPlayerPositionPacket) packet;
handleMovement(position.getX(), position.getY(), position.getZ(), position.isOnGround(), false);
} else if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
return;
}
checkClientTick();
} else if (packet instanceof ConfirmTeleportationPacket) {
@@ -138,15 +133,11 @@ public final class GravityHandler extends VerificationHandler {
checkState(confirmTeleport.getTeleportId() == expectedTeleportId,
"expected TP ID " + expectedTeleportId + ", but got " + confirmTeleport.getTeleportId());
final boolean v26_3 = user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3);
final boolean sendPosRotBefore = user.getProtocolVersion().equals(ProtocolVersion.MINECRAFT_1_21_2);
if (!v26_3) {
checkState(sendPosRotBefore ? lastPositionPacket != null : !expectTeleportPosRot,
"expected position rotation but got teleport confirm.");
if (!sendPosRotBefore) {
expectTeleportPosRot = true;
}
checkState(sendPosRotBefore ? lastPositionPacket != null : !expectTeleportPosRot,
"expected position rotation but got teleport confirm.");
if (!sendPosRotBefore) {
expectTeleportPosRot = true;
}
// The first teleport ID is not useful for us in this context, skip it
@@ -156,11 +147,7 @@ public final class GravityHandler extends VerificationHandler {
// Enable the movement checks
teleported = true;
if (v26_3) {
handleMovement(
confirmTeleport.getX(), confirmTeleport.getY(), confirmTeleport.getZ(),
false, true);
} else if (sendPosRotBefore) {
if (sendPosRotBefore) {
handleMovement(
lastPositionPacket.getX(), lastPositionPacket.getY(), lastPositionPacket.getZ(),
lastPositionPacket.isOnGround(), true);
@@ -261,8 +248,7 @@ public final class GravityHandler extends VerificationHandler {
// The deltaY is 0 whenever the player sends their first position packet.
// We have to account for this or the player will falsely fail the verification.
if (deltaY == 0) {
checkState(user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3) || rotated,
"illegal movement packet order: " + deltaY);
checkState(rotated, "illegal movement packet order: " + deltaY);
checkState(movementTick == 0, "illegal y motion: " + movementTick);
// 1.7 clients immediately start falling after this packet
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_8)) {
@@ -71,8 +71,7 @@ public final class ProtocolHandler extends VerificationHandler {
waitingSlotConfirm = false;
expectedSlotId = -1;
// The player did not send duplicate packets, so they pass this check
// 26.3+ removed client-side swing packet echo, so exempt 26.3+ from the arm animation check
if (user.isGeyser() || user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
if (user.isGeyser()) {
markSuccess();
} else {
sendArmAnimation();
@@ -121,7 +121,7 @@ public abstract class VerificationHandler implements SonarPacketListener {
// The user is allowed to disable the blacklist entirely by setting the threshold to 0
final int limit = Sonar.get0().getConfig().getVerification().getBlacklistThreshold();
// The player hasn't been blacklisted yet, so skip this iteration
if (limit <= 0 || newScore < limit) break blacklist;
if (newScore < limit) break blacklist;
GlobalSonarStatistics.totalBlacklistedPlayers++;
+1 -1
View File
@@ -1,3 +1,3 @@
group=xyz.jonesdev
version=2.1.53
version=2.1.47
description=A lightweight, effective, and easy-to-use anti-bot plugin for Velocity, BungeeCord, and Bukkit.
+6 -5
View File
@@ -1,6 +1,6 @@
[versions]
netty = "4.2.19.Final"
adventure = "5.2.0"
netty = "4.2.15.Final"
adventure = "4.24.0"
adventure-platform = "4.4.1"
bstats = "3.2.1"
libby = "2.0.0-SNAPSHOT"
@@ -8,8 +8,8 @@ pluginyml = "0.6.0"
[plugins]
indra-git = "net.kyori.indra.git:3.2.0"
shadow = "com.gradleup.shadow:9.6.1"
spotless = "com.diffplug.spotless:8.10.4"
shadow = "com.gradleup.shadow:9.4.1"
spotless = "com.diffplug.spotless:8.6.0"
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" }
@@ -20,6 +20,7 @@ adventure-minimessage = { module = "net.kyori:adventure-text-minimessage", versi
adventure-serializer = { module = "net.kyori:adventure-text-serializer-gson", version.ref = "adventure" }
adventure-nbt = { module = "net.kyori:adventure-nbt", version.ref = "adventure" }
adventure-platform-bukkit = { module = "net.kyori:adventure-platform-bukkit", version.ref = "adventure-platform" }
adventure-platform-bungee = { module = "net.kyori:adventure-platform-bungeecord", version.ref = "adventure-platform" }
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" }
@@ -33,7 +34,7 @@ caffeine = "com.github.ben-manes.caffeine:caffeine:3.2.4"
annotations = "org.jetbrains:annotations:26.1.0"
simpleyaml = "com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4"
ormlite = "com.j256.ormlite:ormlite-jdbc:6.1"
lombok = "org.projectlombok:lombok:1.18.48"
lombok = "org.projectlombok:lombok:1.18.46"
bungeecord = "net.md-5:bungeecord-proxy:26.1-R0.1-SNAPSHOT"
velocity = "io.papermc.velocity:velocity-proxy:3.5.0-SNAPSHOT"
paper = "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT"
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
Vendored
+2 -2
View File
@@ -20,7 +20,7 @@
##############################################################################
#
# gradlew start up script for POSIX generated by Gradle.
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh gradlew
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
Vendored
+13 -43
View File
@@ -19,40 +19,13 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem gradlew startup script for Windows
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
@rem Catch executions from older scripts and ensure they exit cleanly.
@rem This can be removed once we can be reasonably confident that few people
@rem will be migrating directly to this new wrapper.
goto afterSafetyNet
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto exitWithErrorLevel
:afterSafetyNet
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
@@ -72,14 +45,13 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
1>&2 echo.
1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
1>&2 echo.
1>&2 echo Please set the JAVA_HOME variable in your environment to match the
1>&2 echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto exitWithErrorLevel
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -87,26 +59,24 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
1>&2 echo.
1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
1>&2 echo.
1>&2 echo Please set the JAVA_HOME variable in your environment to match the
1>&2 echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto exitWithErrorLevel
:execute
@rem Setup the command line
@rem Execute gradlew
@rem Execute Gradle
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
@rem This label must not be changed. We rely on old scripts being able to jump to this point.
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+3
View File
@@ -22,3 +22,6 @@ tasks {
}
}
}
java.sourceCompatibility = JavaVersion.VERSION_17
java.targetCompatibility = JavaVersion.VERSION_17