Compare commits

..
22 Commits
Author SHA1 Message Date
Jones cf865978b4 Release 2.1.52 (#637) 2026-09-27 15:40:37 +02:00
Akkariin eccfb26b30 26.3 Support (#635)
* 26.3 Support

* Add 26.3 protocol version (777)
* Add 26.3 runtime codec and tags
* Update block state IDs for 26.3
* Update item and entity IDs for 26.3
* Update packet registry mappings for 26.3
* Update ConfirmTeleportationPacket, AnimationPacket, EntityAnimationPacket, and JoinGamePacket for 26.3 wire format changes

* Fix non-compound element serialization in RegistryDataPacket

In 26.3, registry entries such as minecraft:block_transformer use ListTag rather than CompoundTag for their element data. Allow any BinaryTag in RegistryDataPacket to prevent empty compound tags from being sent to clients.

* [ci skip] Fix gamemode and previousGamemode encoding in JoinGamePacket for 26.3+

Minecraft 26.3 uses GameType.STREAM_CODEC (VarInt) for gameType and GameType.OPTIONAL_STREAM_CODEC (Optional VarInt where 0 represents none) for previousGameType in CommonPlayerSpawnInfo. Writing previousGamemode as a raw byte (-1 / 0xFF) caused VarInt decoding to consume extra bytes and desynchronize subsequent packet fields.

* Adapt GravityHandler teleport confirmation and movement checks for 26.3+

* Bypass swing arm check in ProtocolHandler for 26.3+

* [ci skip] update annotate for `canFall` field in `GravityHandler`
2026-09-20 13:41:32 +02:00
Jones edf57c280b [skip ci] Update dead wiki.vg link in README 2026-09-15 13:29:29 +02:00
dependabot[bot] 81776712c0 Bump io.netty:netty-all from 4.2.17.Final to 4.2.18.Final (#633)
Bumps [io.netty:netty-all](https://github.com/netty/netty) from 4.2.17.Final to 4.2.18.Final.
- [Release notes](https://github.com/netty/netty/releases)
- [Commits](https://github.com/netty/netty/compare/netty-4.2.17.Final...netty-4.2.18.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-all
  dependency-version: 4.2.18.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-14 11:54:41 +02:00
dependabot[bot] acc3d0df3a Bump com.diffplug.spotless from 8.10.1 to 8.10.2 (#634)
Bumps com.diffplug.spotless from 8.10.1 to 8.10.2.

---
updated-dependencies:
- dependency-name: com.diffplug.spotless
  dependency-version: 8.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-14 11:54:34 +02:00
dependabot[bot] aafb2a22fe Bump org.projectlombok:lombok from 1.18.46 to 1.18.48 (#632)
Bumps [org.projectlombok:lombok](https://github.com/projectlombok/lombok) from 1.18.46 to 1.18.48.
- [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown)
- [Commits](https://github.com/projectlombok/lombok/compare/v1.18.46...v1.18.48)

---
updated-dependencies:
- dependency-name: org.projectlombok:lombok
  dependency-version: 1.18.48
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-07 20:59:59 +02:00
Jones b8bbe411e1 Release 2.1.51 (#630) 2026-09-06 14:57:18 +02:00
purpurcof 4ed63ee2dc Prevent TailExceptionsHandler from racing a graceful close (#616) 2026-08-31 23:05:20 +02:00
dependabot[bot] 7cd7f86114 Bump com.diffplug.spotless from 8.10.0 to 8.10.1 (#629)
Bumps com.diffplug.spotless from 8.10.0 to 8.10.1.

---
updated-dependencies:
- dependency-name: com.diffplug.spotless
  dependency-version: 8.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 11:44:29 +02:00
dependabot[bot] 58c9a6bc25 Bump gradle-wrapper from 9.7.0 to 9.7.1 (#627)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.7.0 to 9.7.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.7.0...v9.7.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 13:01:52 +02:00
dependabot[bot] be6fbc78ab Bump com.diffplug.spotless from 8.9.0 to 8.10.0 (#626)
Bumps com.diffplug.spotless from 8.9.0 to 8.10.0.

---
updated-dependencies:
- dependency-name: com.diffplug.spotless
  dependency-version: 8.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 13:01:43 +02:00
Jones cd5eb191fa Fix filled_map item type ID on 26.2 2026-08-23 14:10:56 +02:00
dependabot[bot] 449ff7a55d Bump io.netty:netty-all from 4.2.16.Final to 4.2.17.Final (#620)
Bumps [io.netty:netty-all](https://github.com/netty/netty) from 4.2.16.Final to 4.2.17.Final.
- [Release notes](https://github.com/netty/netty/releases)
- [Commits](https://github.com/netty/netty/compare/netty-4.2.16.Final...netty-4.2.17.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-all
  dependency-version: 4.2.17.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 15:04:11 +02:00
dependabot[bot] 89b8033540 Bump gradle-wrapper from 9.6.1 to 9.7.0 (#621)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.6.1 to 9.7.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.6.1...v9.7.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 15:03:59 +02:00
dependabot[bot] 1b003e9872 Bump com.diffplug.spotless from 8.8.0 to 8.9.0 (#619)
Bumps com.diffplug.spotless from 8.8.0 to 8.9.0.

---
updated-dependencies:
- dependency-name: com.diffplug.spotless
  dependency-version: 8.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 16:13:07 +02:00
dependabot[bot] 59d1cd2e66 Bump com.gradleup.shadow from 9.6.0 to 9.6.1 (#617)
Bumps [com.gradleup.shadow](https://github.com/GradleUp/shadow) from 9.6.0 to 9.6.1.
- [Release notes](https://github.com/GradleUp/shadow/releases)
- [Commits](https://github.com/GradleUp/shadow/compare/9.6.0...9.6.1)

---
updated-dependencies:
- dependency-name: com.gradleup.shadow
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 13:53:01 +02:00
dependabot[bot] de377dbde6 Bump com.gradleup.shadow from 9.5.1 to 9.6.0 (#614)
Bumps [com.gradleup.shadow](https://github.com/GradleUp/shadow) from 9.5.1 to 9.6.0.
- [Release notes](https://github.com/GradleUp/shadow/releases)
- [Commits](https://github.com/GradleUp/shadow/compare/9.5.1...9.6.0)

---
updated-dependencies:
- dependency-name: com.gradleup.shadow
  dependency-version: 9.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-20 14:09:59 +02:00
Jones 9b5845f5af Implement missing check for blacklist scores of 0 2026-07-15 21:57:00 +02:00
dependabot[bot] 6cd1f9e5e4 Bump com.gradleup.shadow from 9.4.3 to 9.5.1 (#609)
Bumps [com.gradleup.shadow](https://github.com/GradleUp/shadow) from 9.4.3 to 9.5.1.
- [Release notes](https://github.com/GradleUp/shadow/releases)
- [Commits](https://github.com/GradleUp/shadow/compare/9.4.3...9.5.1)

---
updated-dependencies:
- dependency-name: com.gradleup.shadow
  dependency-version: 9.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-13 17:59:34 +02:00
dependabot[bot] 5ca053cfdd Bump io.netty:netty-all from 4.2.15.Final to 4.2.16.Final (#610)
Bumps [io.netty:netty-all](https://github.com/netty/netty) from 4.2.15.Final to 4.2.16.Final.
- [Release notes](https://github.com/netty/netty/releases)
- [Commits](https://github.com/netty/netty/compare/netty-4.2.15.Final...netty-4.2.16.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-all
  dependency-version: 4.2.16.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-13 12:55:43 +02:00
Jones 95d94e467a Release 2.1.50 (#607) 2026-07-12 12:29:30 +02:00
Jones fdd54c9b34 Bump dynamic adventure from 4.21.0 to 5.2.0 2026-07-12 12:20:34 +02:00
25 changed files with 200 additions and 67 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://wiki.vg/Protocol>).
4. Sonar makes sure that players send legitimate packets according to the [vanilla Minecraft protocol](<https://minecraft.wiki/w/Java_Edition_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,7 +77,8 @@ 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_2(776, "26.2"),
MINECRAFT_26_3(777, "26.3");
private final int protocol;
private final String name;
@@ -83,7 +83,8 @@ public enum BukkitServerVersion {
MINECRAFT_1_21_10,
MINECRAFT_1_21_11,
MINECRAFT_26_1,
MINECRAFT_26_2;
MINECRAFT_26_2,
MINECRAFT_26_3;
static final BukkitServerVersion[] REVERSED_VALUES;
@@ -73,19 +73,19 @@ class LibraryLoader {
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-minimessage")
.version("4.21.0")
.version("5.2.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-text-serializer-gson")
.version("4.21.0")
.version("5.2.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build(),
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-nbt")
.version("4.21.0")
.version("5.2.0")
.relocate("net{}kyori", "xyz{}jonesdev{}sonar{}libs{}kyori")
.build()
);
@@ -95,7 +95,7 @@ class LibraryLoader {
Library.builder()
.groupId("net{}kyori")
.artifactId("adventure-nbt")
.version("4.21.0")
.version("5.2.0")
.relocate("net{}kyori{}adventure{}nbt", "xyz{}jonesdev{}sonar{}libs{}kyori{}adventure{}nbt")
.build()
);
@@ -21,6 +21,7 @@ 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 {
@@ -33,6 +34,20 @@ 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,8 +106,10 @@ 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()));
@@ -296,7 +298,9 @@ public class SonarPacketPreparer {
}
public static @Nullable SonarPacket getTagsPacket(final @NotNull ProtocolVersion protocolVersion) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_2)) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
return TAGS_UPDATE_26_3;
} else 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;
@@ -305,7 +309,9 @@ public class SonarPacketPreparer {
}
public static SonarPacket[] getRegistryPackets(final @NotNull ProtocolVersion protocolVersion) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_2)) {
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3)) {
return REGISTRY_SYNC_26_3;
} else 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,7 +77,8 @@ 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(0x0D, MINECRAFT_1_20_5, true),
map(0x0E, MINECRAFT_26_3, true));
serverbound.register(ClientInformationPacket.class, ClientInformationPacket::new,
map(0x00, MINECRAFT_1_20_2, false));
@@ -113,7 +114,8 @@ 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(0x31, MINECRAFT_26_1, true),
map(0x32, MINECRAFT_26_3, true));
// minecraft:keep_alive
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
map(0x00, MINECRAFT_1_7_2, true),
@@ -133,7 +135,8 @@ 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(0x2C, MINECRAFT_26_1, true),
map(0x2D, MINECRAFT_26_3, true));
// minecraft:disconnect
clientbound.register(DisconnectPacket.class, DisconnectPacket::new,
map(0x40, MINECRAFT_1_7_2, true),
@@ -172,7 +175,8 @@ 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(0x48, MINECRAFT_26_1, true),
map(0x49, MINECRAFT_26_3, true));
// minecraft:player_abilities
clientbound.register(PlayerAbilitiesPacket.class, PlayerAbilitiesPacket::new,
map(0x39, MINECRAFT_1_7_2, true),
@@ -193,7 +197,8 @@ 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(0x40, MINECRAFT_26_1, true),
map(0x41, MINECRAFT_26_3, true));
// minecraft:level_chunk_with_light
clientbound.register(ChunkDataPacket.class, ChunkDataPacket::new,
map(0x21, MINECRAFT_1_7_2, true),
@@ -213,7 +218,8 @@ 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(0x2D, MINECRAFT_26_1, true),
map(0x2E, MINECRAFT_26_3, true));
// minecraft:remove_entities
clientbound.register(RemoveEntitiesPacket.class, RemoveEntitiesPacket::new,
map(0x13, MINECRAFT_1_7_2, true),
@@ -236,7 +242,8 @@ 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(0x4D, MINECRAFT_26_1, true),
map(0x4E, MINECRAFT_26_3, true));
// minecraft:set_passengers
clientbound.register(SetPassengersPacket.class, SetPassengersPacket::new,
map(0x1B, MINECRAFT_1_7_2, true),
@@ -256,7 +263,8 @@ 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(0x6B, MINECRAFT_26_1, true),
map(0x6D, MINECRAFT_26_3, true));
// minecraft:add_entity
clientbound.register(SpawnEntityPacket.class, SpawnEntityPacket::new,
map(0x0E, MINECRAFT_1_7_2, true),
@@ -281,7 +289,8 @@ 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(0x54, MINECRAFT_26_1, true),
map(0x56, MINECRAFT_26_3, true));
// minecraft:ping
clientbound.register(TransactionPacket.class, TransactionPacket::new,
map(0x32, MINECRAFT_1_7_2, true),
@@ -300,7 +309,8 @@ 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(0x3D, MINECRAFT_26_1, true),
map(0x3E, MINECRAFT_26_3, true));
// minecraft:set_default_spawn_position
clientbound.register(SetDefaultSpawnPositionPacket.class, SetDefaultSpawnPositionPacket::new,
map(0x05, MINECRAFT_1_7_2, true),
@@ -322,7 +332,8 @@ 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(0x61, MINECRAFT_26_1, true),
map(0x63, MINECRAFT_26_3, true));
// minecraft:map_item_data
clientbound.register(MapDataPacket.class, MapDataPacket::new,
map(0x34, MINECRAFT_1_7_2, true),
@@ -341,7 +352,8 @@ 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(0x33, MINECRAFT_26_1, true),
map(0x34, MINECRAFT_26_3, true));
// minecraft:container_set_slot
clientbound.register(SetContainerSlotPacket.class, SetContainerSlotPacket::new,
map(0x2F, MINECRAFT_1_7_2, true),
@@ -376,7 +388,8 @@ 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(0x67, MINECRAFT_26_1, true),
map(0x69, MINECRAFT_26_3, true));
// minecraft:system_chat
clientbound.register(SystemChatPacket.class, SystemChatPacket::new,
map(0x02, MINECRAFT_1_7_2, true),
@@ -395,20 +408,23 @@ 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(0x79, MINECRAFT_26_1, true),
map(0x7C, MINECRAFT_26_3, 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(0x26, MINECRAFT_1_21_9, true),
map(0x27, MINECRAFT_26_3, 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(0x81, MINECRAFT_26_1, true),
map(0x84, MINECRAFT_26_3, true));
// minecraft:set_held_slot
clientbound.register(SetHeldItemPacket.class, SetHeldItemPacket::new,
map(0x09, MINECRAFT_1_7_2, true),
@@ -430,7 +446,8 @@ 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(0x69, MINECRAFT_26_1, true),
map(0x6B, MINECRAFT_26_3, true));
// minecraft:animate
clientbound.register(EntityAnimationPacket.class, EntityAnimationPacket::new,
map(0x0B, MINECRAFT_1_7_2, true),
@@ -462,7 +479,8 @@ 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(0x71, MINECRAFT_26_1, true),
map(0x73, MINECRAFT_26_3, true));
// minecraft:client_tick_end
serverbound.register(ClientTickEndPacket.class, ClientTickEndPacket::new,
@@ -486,7 +504,8 @@ 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(0x35, MINECRAFT_26_1, false),
map(0x36, MINECRAFT_26_3, false));
// minecraft:paddle_boat
serverbound.register(PaddleBoatPacket.class, PaddleBoatPacket::new,
map(0x11, MINECRAFT_1_9, false),
@@ -525,7 +544,8 @@ 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(0x26, MINECRAFT_26_1, false));
map(0x26, MINECRAFT_26_1, false),
map(0x2B, MINECRAFT_26_3, false));
// minecraft:move_vehicle
serverbound.register(VehicleMovePacket.class, VehicleMovePacket::new,
map(0x10, MINECRAFT_1_9, false),
@@ -734,7 +754,8 @@ 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(0x3F, MINECRAFT_26_1, false),
map(0x2E, MINECRAFT_26_3, false));
}
};
@@ -80,7 +80,10 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 9250;
}
return 9451;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 9451;
}
return 11185;
}, protocolVersion -> (double) 0.75f),
TRAPDOOR(protocolVersion -> {
// We have to use wooden trapdoors for 1.7 since 1.7 doesn't have iron trapdoors
@@ -135,7 +138,10 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 12380;
}
return 12582;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 12582;
}
return 14429;
}, protocolVersion -> 0.1875),
END_PORTAL_FRAME(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -183,7 +189,10 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 9272;
}
return 9473;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 9473;
}
return 11207;
}, protocolVersion -> 0.8125),
DAYLIGHT_SENSOR(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -234,7 +243,10 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 11093;
}
return 11295;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 11295;
}
return 13062;
}, protocolVersion -> 0.375),
COBBLESTONE_WALL(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -282,7 +294,10 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 9783;
}
return 9984;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 9984;
}
return 11726;
}, protocolVersion -> 1.5),
STONE_SLABS(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -333,7 +348,10 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 13197;
}
return 13399;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 13399;
}
return 15252;
}, protocolVersion -> 0.5),
WHITE_CARPET(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
@@ -384,7 +402,10 @@ public enum BlockType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 12694;
}
return 12896;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 12896;
}
return 14743;
}, protocolVersion -> protocolVersion.compareTo(MINECRAFT_1_8) < 0 ? 0 : 0.0625);
private final Function<ProtocolVersion, Integer> id;
@@ -43,8 +43,10 @@ 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");
@@ -62,8 +64,10 @@ 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,7 +67,10 @@ public enum EntityType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
return 87;
}
return 89;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 89;
}
return 90;
}),
MINECART(protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
@@ -112,7 +115,10 @@ public enum EntityType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
return 84;
}
return 85;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 85;
}
return 86;
});
private final Function<ProtocolVersion, Integer> id;
@@ -85,7 +85,13 @@ public enum ItemType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 1104;
}
return 1105;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_1)) {
return 1105;
}
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 1133;
}
return 1238;
}, protocolVersion -> {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
return 26;
@@ -99,7 +105,10 @@ public enum ItemType {
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_11)) {
return 44;
}
return 46;
if (protocolVersion.lessThanOrEquals(MINECRAFT_26_2)) {
return 46;
}
return 48;
});
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 CompoundBinaryTag tag = bundle.getTag();
final BinaryTag tag = bundle.getTag();
if (tag != null) {
byteBuf.writeBoolean(true);
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, tag);
@@ -73,8 +73,7 @@ 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;
// 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")));
bundles.add(new Bundle(tag.getString("name"), tag.get("element")));
}
packets[index++] = new SonarPacketSnapshot(new RegistryDataPacket(rootTag, type, bundles));
}
@@ -86,6 +85,6 @@ public final class RegistryDataPacket implements SonarPacket {
public static class Bundle {
@NotNull String name;
// In the protocol, it is able to be null
@Nullable CompoundBinaryTag tag;
@Nullable BinaryTag tag;
}
}
@@ -46,6 +46,8 @@ 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,6 +32,15 @@ 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) {
@@ -41,5 +50,12 @@ 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,8 +148,14 @@ public final class JoinGamePacket implements SonarPacket {
ProtocolUtil.writeString(byteBuf, levelName);
byteBuf.writeLong(partialHashedSeed);
byteBuf.writeByte(gamemode);
byteBuf.writeByte(previousGamemode);
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);
}
}
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16)) {
@@ -169,12 +175,11 @@ 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,6 +24,7 @@ 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;
@@ -217,9 +218,20 @@ 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,8 +36,9 @@ public final class GravityHandler extends VerificationHandler {
super(user);
this.preJoinHandler = preJoinHandler;
// Bedrock users start falling immediately
this.canFall = user.isGeyser();
// 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);
// 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());
@@ -108,6 +109,8 @@ 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;
@@ -122,6 +125,8 @@ 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) {
@@ -133,11 +138,15 @@ 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);
checkState(sendPosRotBefore ? lastPositionPacket != null : !expectTeleportPosRot,
"expected position rotation but got teleport confirm.");
if (!sendPosRotBefore) {
expectTeleportPosRot = true;
if (!v26_3) {
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
@@ -147,7 +156,11 @@ public final class GravityHandler extends VerificationHandler {
// Enable the movement checks
teleported = true;
if (sendPosRotBefore) {
if (v26_3) {
handleMovement(
confirmTeleport.getX(), confirmTeleport.getY(), confirmTeleport.getZ(),
false, true);
} else if (sendPosRotBefore) {
handleMovement(
lastPositionPacket.getX(), lastPositionPacket.getY(), lastPositionPacket.getZ(),
lastPositionPacket.isOnGround(), true);
@@ -248,7 +261,8 @@ 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(rotated, "illegal movement packet order: " + deltaY);
checkState(user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_26_3) || 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,7 +71,8 @@ public final class ProtocolHandler extends VerificationHandler {
waitingSlotConfirm = false;
expectedSlotId = -1;
// The player did not send duplicate packets, so they pass this check
if (user.isGeyser()) {
// 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)) {
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 (newScore < limit) break blacklist;
if (limit <= 0 || newScore < limit) break blacklist;
GlobalSonarStatistics.totalBlacklistedPlayers++;
+1 -1
View File
@@ -1,3 +1,3 @@
group=xyz.jonesdev
version=2.1.49
version=2.1.52
description=A lightweight, effective, and easy-to-use anti-bot plugin for Velocity, BungeeCord, and Bukkit.
+4 -4
View File
@@ -1,5 +1,5 @@
[versions]
netty = "4.2.15.Final"
netty = "4.2.18.Final"
adventure = "5.2.0"
adventure-platform = "4.4.1"
bstats = "3.2.1"
@@ -8,8 +8,8 @@ pluginyml = "0.6.0"
[plugins]
indra-git = "net.kyori.indra.git:3.2.0"
shadow = "com.gradleup.shadow:9.4.3"
spotless = "com.diffplug.spotless:8.8.0"
shadow = "com.gradleup.shadow:9.6.1"
spotless = "com.diffplug.spotless:8.10.2"
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" }
@@ -34,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.46"
lombok = "org.projectlombok:lombok:1.18.48"
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.6.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500