Compare commits

..
2 Commits
Author SHA1 Message Date
Jones 1438856531 Release 2.1.6 (#379) 2024-09-11 16:36:00 +02:00
AkkariinandJones 694725b798 Implement more interactionless checks (#378)
* Add minecart entity

* Add `VehicleMovePacket`

* Add `Animation` packet

* Add animation check.

* Fix the wrong entity minecart id for 1.21

* Add minecart for vehicle check.

* Exempt minecart check for 1.7-1.8 clients.

* Fix minecart id for lower than minecraft version 1.14.

* Fix wrong minecart entity id for 1.15

* Add missing license header

* Exempt animation check for 1.7.2 client due unknown disconnect error.

* style: small code style improvements

* style: even more style improvements

* refactor: inline some stuff

* style: small code cleanup

* fix: slot check not working in the first place

* style: don't use return; use an if-else block

* Fix animation check not working on 1.7.2+

* style: minor code style improvements

* fix: lombok compile error

* style: minor code style adjustments

* fix: optimize AnimationPacket

* fix: don't reassign unnecessary hand value

* style: code style fixes in *types

* style: add some comments to the *types

* [ci skip] style: small code style improvements

* Don't exempt 1.7-1.8 from the Minecart check

---------

Co-authored-by: Jones <admin@jonesdev.xyz>
2024-09-11 16:29:30 +02:00
11 changed files with 513 additions and 68 deletions

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
group=xyz.jonesdev.sonar
version=2.1.5
version=2.1.6
description=A lightweight, effective, and easy-to-use anti-bot plugin for Velocity, BungeeCord, and Bukkit.
@@ -213,6 +213,20 @@ public enum FallbackPacketRegistry {
map(0x5B, MINECRAFT_1_20_2, true),
map(0x5D, MINECRAFT_1_20_3, true),
map(0x5F, MINECRAFT_1_20_5, true));
clientbound.register(VehicleMovePacket.class, VehicleMovePacket::new,
map(0x29, MINECRAFT_1_9, false),
map(0x2B, MINECRAFT_1_13, false),
map(0x2C, MINECRAFT_1_14, false),
map(0x2D, MINECRAFT_1_15, false),
map(0x2C, MINECRAFT_1_16, false),
map(0x2B, MINECRAFT_1_16_2, false),
map(0x2C, MINECRAFT_1_17, false),
map(0x29, MINECRAFT_1_19, false),
map(0x2B, MINECRAFT_1_19_1, false),
map(0x2A, MINECRAFT_1_19_3, false),
map(0x2E, MINECRAFT_1_19_4, false),
map(0x2F, MINECRAFT_1_20_2, false),
map(0x31, MINECRAFT_1_20_5, false));
clientbound.register(SpawnEntityPacket.class, SpawnEntityPacket::new,
map(0x0E, MINECRAFT_1_7_2, true),
map(0x00, MINECRAFT_1_9, true),
@@ -341,6 +355,14 @@ public enum FallbackPacketRegistry {
map(0x4F, MINECRAFT_1_20_2, true),
map(0x51, MINECRAFT_1_20_3, true),
map(0x53, MINECRAFT_1_20_5, true));
clientbound.register(EntityAnimationPacket.class, EntityAnimationPacket::new,
map(0x0B, MINECRAFT_1_7_2, true),
map(0x06, MINECRAFT_1_9, true),
map(0x05, MINECRAFT_1_16, true),
map(0x06, MINECRAFT_1_17, true),
map(0x03, MINECRAFT_1_19, true),
map(0x04, MINECRAFT_1_19_4, true),
map(0x03, MINECRAFT_1_20_2, true));
serverbound.register(SetHeldItemPacket.class, SetHeldItemPacket::new,
map(0x09, MINECRAFT_1_7_2, false),
@@ -385,6 +407,21 @@ public enum FallbackPacketRegistry {
map(0x22, MINECRAFT_1_20_2, false),
map(0x23, MINECRAFT_1_20_3, false),
map(0x26, MINECRAFT_1_20_5, false));
serverbound.register(VehicleMovePacket.class, VehicleMovePacket::new,
map(0x10, MINECRAFT_1_9, false),
map(0x11, MINECRAFT_1_12, false),
map(0x10, MINECRAFT_1_12_1, false),
map(0x13, MINECRAFT_1_13, false),
map(0x15, MINECRAFT_1_14, false),
map(0x16, MINECRAFT_1_16, false),
map(0x15, MINECRAFT_1_17, false),
map(0x17, MINECRAFT_1_19, false),
map(0x18, MINECRAFT_1_19_1, false),
map(0x17, MINECRAFT_1_19_3, false),
map(0x18, MINECRAFT_1_19_4, false),
map(0x1A, MINECRAFT_1_20_2, false),
map(0x1B, MINECRAFT_1_20_3, false),
map(0x1E, MINECRAFT_1_20_5, false));
serverbound.register(SystemChatPacket.class, SystemChatPacket::new,
map(0x01, MINECRAFT_1_7_2, false),
map(0x02, MINECRAFT_1_9, false),
@@ -518,6 +555,19 @@ public enum FallbackPacketRegistry {
map(0x23, MINECRAFT_1_20_2, false),
map(0x24, MINECRAFT_1_20_3, false),
map(0x27, MINECRAFT_1_20_5, false));
serverbound.register(AnimationPacket.class, AnimationPacket::new,
map(0x0A, MINECRAFT_1_7_2, false),
map(0x1A, MINECRAFT_1_9, false),
map(0x1D, MINECRAFT_1_12, false),
map(0x27, MINECRAFT_1_13, false),
map(0x2A, MINECRAFT_1_14, false),
map(0x2B, MINECRAFT_1_16, false),
map(0x2C, MINECRAFT_1_16_2, false),
map(0x2E, MINECRAFT_1_19, false),
map(0x2F, MINECRAFT_1_19_1, false),
map(0x32, MINECRAFT_1_20_2, false),
map(0x33, MINECRAFT_1_20_3, false),
map(0x36, MINECRAFT_1_20_5, false));
}
};
@@ -62,7 +62,7 @@ public class FallbackPreparer {
public FallbackPacket enterCodeMessage;
public FallbackPacket incorrectCaptcha;
// JoinGame
public final int PLAYER_ENTITY_ID = RANDOM.nextInt(10);
public static final int PLAYER_ENTITY_ID = RANDOM.nextInt(50);
public FallbackPacket joinGame;
// Update Section Blocks
public FallbackPacket updateSectionBlocks;
@@ -84,10 +84,14 @@ public class FallbackPreparer {
public FallbackPacket tooManyOnlinePerIP;
// Vehicle
public FallbackPacket removeEntities;
public FallbackPacket spawnEntity;
public static FallbackPacket setPassengers;
public static final int VEHICLE_ENTITY_ID = PLAYER_ENTITY_ID + 1 + RANDOM.nextInt(10);
public FallbackPacket removeBoatEntities;
public FallbackPacket removeMinecartEntities;
public FallbackPacket spawnBoatEntity;
public FallbackPacket spawnMinecartEntity;
public FallbackPacket setBoatPassengers;
public FallbackPacket setMinecartPassengers;
public final int VEHICLE_BOAT_ENTITY_ID = PLAYER_ENTITY_ID + 1 + RANDOM.nextInt(10);
public final int VEHICLE_MINECART_ENTITY_ID = VEHICLE_BOAT_ENTITY_ID + 1 + RANDOM.nextInt(10);
// Collisions
public final int BLOCKS_PER_ROW = 8; // 8 * 8 = 64 (protocol maximum)
@@ -181,10 +185,14 @@ public class FallbackPreparer {
}
// Prepare packets for the vehicle check
removeEntities = new FallbackPacketSnapshot(new RemoveEntitiesPacket(VEHICLE_ENTITY_ID));
spawnEntity = new FallbackPacketSnapshot(new SpawnEntityPacket(
VEHICLE_ENTITY_ID, EntityType.BOAT, SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION));
setPassengers = new FallbackPacketSnapshot(new SetPassengersPacket(VEHICLE_ENTITY_ID, PLAYER_ENTITY_ID));
removeBoatEntities = new FallbackPacketSnapshot(new RemoveEntitiesPacket(VEHICLE_BOAT_ENTITY_ID));
removeMinecartEntities = new FallbackPacketSnapshot(new RemoveEntitiesPacket(VEHICLE_MINECART_ENTITY_ID));
spawnBoatEntity = new FallbackPacketSnapshot(new SpawnEntityPacket(
VEHICLE_BOAT_ENTITY_ID, EntityType.BOAT, SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION));
spawnMinecartEntity = new FallbackPacketSnapshot(new SpawnEntityPacket(
VEHICLE_MINECART_ENTITY_ID, EntityType.MINECART, SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION));
setBoatPassengers = new FallbackPacketSnapshot(new SetPassengersPacket(VEHICLE_BOAT_ENTITY_ID, PLAYER_ENTITY_ID));
setMinecartPassengers = new FallbackPacketSnapshot(new SetPassengersPacket(VEHICLE_MINECART_ENTITY_ID, PLAYER_ENTITY_ID));
if (Sonar.get().getConfig().getVerification().getMap().getTiming() != SonarConfiguration.Verification.Timing.NEVER
|| Sonar.get().getConfig().getVerification().getGravity().isCaptchaOnFail()) {
@@ -27,108 +27,128 @@ import java.util.function.Function;
@SuppressWarnings("unused")
@RequiredArgsConstructor
public enum BlockType {
STONE(protocolVersion -> 1, 1),
// Useful resources:
// - https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/
// - https://pokechu22.github.io/Burger/
// - https://github.com/ViaVersion/Mappings/tree/main/mappings
STONE(protocolVersion -> 1, 1),
ENCHANTMENT_TABLE(protocolVersion -> {
switch (protocolVersion) {
case MINECRAFT_1_7_2:
case MINECRAFT_1_7_6:
default:
// 1.7.2-1.12.2
return 116;
// 1.13-1.13.1
case MINECRAFT_1_13:
case MINECRAFT_1_13_1:
return 4612;
// 1.13.2-1.14.3
case MINECRAFT_1_13_2:
case MINECRAFT_1_14:
case MINECRAFT_1_14_1:
case MINECRAFT_1_14_2:
case MINECRAFT_1_14_3:
return 4613;
// 1.14.4-1.15.2
case MINECRAFT_1_14_4:
case MINECRAFT_1_15:
case MINECRAFT_1_15_1:
case MINECRAFT_1_15_2:
return 5116;
// 1.16-1.16.1
case MINECRAFT_1_16:
case MINECRAFT_1_16_1:
return 5132;
// 1.16.2-1.16.4
case MINECRAFT_1_16_2:
case MINECRAFT_1_16_3:
case MINECRAFT_1_16_4:
return 5136;
// 1.17-1.18.2
case MINECRAFT_1_17:
case MINECRAFT_1_17_1:
case MINECRAFT_1_18:
case MINECRAFT_1_18_2:
return 5333;
// 1.19-1.19.1
case MINECRAFT_1_19:
case MINECRAFT_1_19_1:
return 5719;
// 1.19.3
case MINECRAFT_1_19_3:
return 7159;
// 1.19.4
case MINECRAFT_1_19_4:
return 7385;
// 1.20-1.21
case MINECRAFT_1_20:
case MINECRAFT_1_20_2:
case MINECRAFT_1_20_3:
case MINECRAFT_1_20_5:
case MINECRAFT_1_21:
return 7389;
// 1.7.2-1.12.2
default:
return 116;
}
}, 0.75f),
TRAPDOOR(protocolVersion -> {
switch (protocolVersion) {
// 1.7.2-1.7.6
case MINECRAFT_1_7_2:
case MINECRAFT_1_7_6:
// We have to use wooden trapdoors for 1.7 since 1.7 doesn't have iron trapdoors
return 96;
default:
// 1.8-1.12.2
return 167;
// 1.13-1.13.1
case MINECRAFT_1_13:
case MINECRAFT_1_13_1:
return 6509;
// 1.13.2-1.14.3
case MINECRAFT_1_13_2:
case MINECRAFT_1_14:
case MINECRAFT_1_14_1:
case MINECRAFT_1_14_2:
case MINECRAFT_1_14_3:
return 6510;
// 1.14.4-1.15.2
case MINECRAFT_1_14_4:
case MINECRAFT_1_15:
case MINECRAFT_1_15_1:
case MINECRAFT_1_15_2:
return 7016;
// 1.16-1.16.1
case MINECRAFT_1_16:
case MINECRAFT_1_16_1:
return 7552;
// 1.16.2-1.16.4
case MINECRAFT_1_16_2:
case MINECRAFT_1_16_3:
case MINECRAFT_1_16_4:
return 7556;
// 1.17-1.18.2
case MINECRAFT_1_17:
case MINECRAFT_1_17_1:
case MINECRAFT_1_18:
case MINECRAFT_1_18_2:
return 7802;
// 1.19-1.19.1
case MINECRAFT_1_19:
case MINECRAFT_1_19_1:
return 8293;
// 1.19.3
case MINECRAFT_1_19_3:
return 9937;
// 1.19.4
case MINECRAFT_1_19_4:
return 10269;
// 1.20
case MINECRAFT_1_20:
return 10273;
// 1.20.2-1.21
case MINECRAFT_1_20_2:
case MINECRAFT_1_20_3:
case MINECRAFT_1_20_5:
case MINECRAFT_1_21:
return 10414;
// 1.8-1.12.2
default:
return 167;
}
}, 0.1875),
BARRIER(protocolVersion -> {
@@ -137,14 +157,14 @@ public enum BlockType {
case MINECRAFT_1_7_6:
// We have to use glass for 1.7 since 1.7 doesn't have barrier blocks
return 20;
default:
// 1.8-1.12.2
return 166;
// 1.13-1.13.1
case MINECRAFT_1_13:
case MINECRAFT_1_13_1:
return 6493;
// 1.13.2
case MINECRAFT_1_13_2:
return 6494;
// 1.14-1.15.2
case MINECRAFT_1_14:
case MINECRAFT_1_14_1:
case MINECRAFT_1_14_2:
@@ -154,37 +174,47 @@ public enum BlockType {
case MINECRAFT_1_15_1:
case MINECRAFT_1_15_2:
return 7000;
// 1.16-1.16.1
case MINECRAFT_1_16:
case MINECRAFT_1_16_1:
return 7536;
// 1.16.2-1.16.4
case MINECRAFT_1_16_2:
case MINECRAFT_1_16_3:
case MINECRAFT_1_16_4:
return 7540;
// 1.17-1.18.2
case MINECRAFT_1_17:
case MINECRAFT_1_17_1:
case MINECRAFT_1_18:
case MINECRAFT_1_18_2:
return 7754;
// 1.19-1.19.1
case MINECRAFT_1_19:
case MINECRAFT_1_19_1:
return 8245;
// 1.19.3
case MINECRAFT_1_19_3:
return 9889;
// 1.19.4
case MINECRAFT_1_19_4:
return 10221;
// 1.20
case MINECRAFT_1_20:
return 10225;
// 1.20.2-1.21
case MINECRAFT_1_20_2:
case MINECRAFT_1_20_3:
case MINECRAFT_1_20_5:
case MINECRAFT_1_21:
return 10366;
// 1.8-1.12.2
default:
return 166;
}
}, 1);
private final Function<ProtocolVersion, Integer> idFunction;
// TODO: Implement per-version block heights?
@Getter
private final double blockHeight;
@@ -26,20 +26,20 @@ import java.util.function.Function;
@SuppressWarnings("unused")
@RequiredArgsConstructor
public enum ItemType {
// Useful resources:
// - https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/
// - https://pokechu22.github.io/Burger/1.20.4.html
// - https://github.com/ViaVersion/Mappings/tree/main/mappings
FILLED_MAP(protocolVersion -> {
// Useful resources:
// - https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/
// - https://pokechu22.github.io/Burger/1.20.4.html
// - https://github.com/ViaVersion/Mappings/tree/main/mappings
switch (protocolVersion) {
default:
// 1.7-1.12.2
return 358;
// 1.13-1.13.1
case MINECRAFT_1_13:
case MINECRAFT_1_13_1:
return 608;
// 1.13.2
case MINECRAFT_1_13_2:
return 613;
// 1.14-1.15.2
case MINECRAFT_1_14:
case MINECRAFT_1_14_1:
case MINECRAFT_1_14_2:
@@ -49,38 +49,49 @@ public enum ItemType {
case MINECRAFT_1_15_1:
case MINECRAFT_1_15_2:
return 671;
// 1.16-1.16.4
case MINECRAFT_1_16:
case MINECRAFT_1_16_1:
case MINECRAFT_1_16_2:
case MINECRAFT_1_16_3:
case MINECRAFT_1_16_4:
return 733;
// 1.17-1.18.2
case MINECRAFT_1_17:
case MINECRAFT_1_17_1:
case MINECRAFT_1_18:
case MINECRAFT_1_18_2:
return 847;
// 1.19-1.19.1
case MINECRAFT_1_19:
case MINECRAFT_1_19_1:
return 886;
// 1.19.3
case MINECRAFT_1_19_3:
return 914;
// 1.19.4
case MINECRAFT_1_19_4:
return 937;
// 1.20-1.20.2
case MINECRAFT_1_20:
case MINECRAFT_1_20_2:
return 941;
// 1.20.3
case MINECRAFT_1_20_3:
return 979;
// 1.20.5-1.21
case MINECRAFT_1_20_5:
case MINECRAFT_1_21:
return 982;
default:
// 1.7-1.12.2
return 358;
}
});
private final Function<ProtocolVersion, Integer> function;
private final Function<ProtocolVersion, Integer> idFunction;
public int getId(final @NotNull ProtocolVersion protocolVersion) {
return function.apply(protocolVersion);
return idFunction.apply(protocolVersion);
}
}
@@ -25,35 +25,19 @@ import java.util.function.Function;
@RequiredArgsConstructor
public enum EntityType {
// Useful resources:
// - https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/
// - https://pokechu22.github.io/Burger/1.20.4.html
BOAT(protocolVersion -> {
// Useful resources:
// - https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/
// - https://pokechu22.github.io/Burger/1.20.4.html
switch (protocolVersion) {
default:
case MINECRAFT_1_7_2:
case MINECRAFT_1_7_6:
case MINECRAFT_1_8:
case MINECRAFT_1_9:
case MINECRAFT_1_9_1:
case MINECRAFT_1_9_2:
case MINECRAFT_1_9_4:
case MINECRAFT_1_10:
case MINECRAFT_1_11:
case MINECRAFT_1_11_1:
case MINECRAFT_1_12:
case MINECRAFT_1_12_1:
case MINECRAFT_1_12_2:
case MINECRAFT_1_13:
case MINECRAFT_1_13_1:
case MINECRAFT_1_13_2:
return 1;
// 1.14-1.14.4
case MINECRAFT_1_14:
case MINECRAFT_1_14_1:
case MINECRAFT_1_14_2:
case MINECRAFT_1_14_3:
case MINECRAFT_1_14_4:
return 5;
// 1.15-1.16.4
case MINECRAFT_1_15:
case MINECRAFT_1_15_1:
case MINECRAFT_1_15_2:
@@ -63,23 +47,81 @@ public enum EntityType {
case MINECRAFT_1_16_3:
case MINECRAFT_1_16_4:
return 6;
// 1.17-1.18.2
case MINECRAFT_1_17:
case MINECRAFT_1_17_1:
case MINECRAFT_1_18:
case MINECRAFT_1_18_2:
return 7;
// 1.19-1.19.3
case MINECRAFT_1_19:
case MINECRAFT_1_19_1:
case MINECRAFT_1_19_3:
return 8;
// 1.19.4-1.20.3
case MINECRAFT_1_19_4:
case MINECRAFT_1_20:
case MINECRAFT_1_20_2:
case MINECRAFT_1_20_3:
return 9;
// 1.20.5-1.21
case MINECRAFT_1_20_5:
case MINECRAFT_1_21:
return 10;
// 1.7-1.13.2
default:
return 1;
}
}),
MINECART(protocolVersion -> {
switch (protocolVersion) {
// 1.14-1.14.4
case MINECRAFT_1_14:
case MINECRAFT_1_14_1:
case MINECRAFT_1_14_2:
case MINECRAFT_1_14_3:
case MINECRAFT_1_14_4:
return 41;
// 1.15-1.15.2
case MINECRAFT_1_15:
case MINECRAFT_1_15_1:
case MINECRAFT_1_15_2:
return 42;
// 1.16-1.16.4
case MINECRAFT_1_16:
case MINECRAFT_1_16_1:
case MINECRAFT_1_16_2:
case MINECRAFT_1_16_3:
case MINECRAFT_1_16_4:
return 45;
// 1.17-1.18.2
case MINECRAFT_1_17:
case MINECRAFT_1_17_1:
case MINECRAFT_1_18:
case MINECRAFT_1_18_2:
return 50;
// 1.19-1.19.1
case MINECRAFT_1_19:
case MINECRAFT_1_19_1:
return 53;
// 1.19.3
case MINECRAFT_1_19_3:
return 54;
// 1.19.4-1.20.2
case MINECRAFT_1_19_4:
case MINECRAFT_1_20:
case MINECRAFT_1_20_2:
return 64;
// 1.20.3
case MINECRAFT_1_20_3:
return 65;
// 1.20.5-1.21
case MINECRAFT_1_20_5:
case MINECRAFT_1_21:
return 69;
// 1.7-1.13.2
default:
return 10;
}
});
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2024 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.fallback.protocol.packets.play;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public final class AnimationPacket implements FallbackPacket {
private int entityId = -1;
private int hand = MAIN_HAND;
private LegacyAnimationType type = LegacyAnimationType.SWING_ARM;
public static final int MAIN_HAND = 0;
@Override
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public void decode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
entityId = byteBuf.readInt();
type = LegacyAnimationType.getById(byteBuf.readByte());
} else if (protocolVersion.compareTo(MINECRAFT_1_8) > 0) {
// Only 1.9+ clients have an offhand
hand = readVarInt(byteBuf);
}
}
@Getter
public enum LegacyAnimationType {
NO_ANIMATION,
SWING_ARM,
DAMAGE_ANIMATION,
LEAVE_BED,
EAT_FOOD,
CRITICAL_EFFECT,
MAGIC_CRITICAL_EFFECT,
UNKNOWN(102),
CROUCH(104),
@SuppressWarnings("SpellCheckingInspection")
UNCROUCH(105);
private final int id;
LegacyAnimationType(int id) {
this.id = id;
}
LegacyAnimationType() {
this.id = ordinal();
}
public static LegacyAnimationType getById(int id) {
if (id >= 0 && id <= 7) {
return LegacyAnimationType.values()[id];
}
switch (id) {
case 102:
return UNKNOWN;
case 104:
return CROUCH;
case 105:
return UNCROUCH;
default:
throw new IllegalArgumentException("Unknown type with id: " + id);
}
}
}
}
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 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.fallback.protocol.packets.play;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public final class EntityAnimationPacket implements FallbackPacket {
private int entityId;
private Type type;
@Override
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
writeVarInt(byteBuf, entityId);
byteBuf.writeByte(type.ordinal());
}
@Override
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
throw new UnsupportedOperationException();
}
public enum Type {
SWING_MAIN_ARM,
HURT,
WAKE_UP,
// 1.9+?
SWING_OFF_HAND, // Eat food on 1.7
CRITICAL_HIT,
MAGIC_CRITICAL_HIT
// unknown (102), crouch (104), uncrouch(105) only exist on 1.7 and unused here
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2024 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.fallback.protocol.packets.play;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public final class VehicleMovePacket implements FallbackPacket {
private double x, y, z;
private float yaw, pitch;
@Override
public void encode(final @NotNull ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
byteBuf.writeDouble(x);
byteBuf.writeDouble(y);
byteBuf.writeDouble(z);
byteBuf.writeFloat(yaw);
byteBuf.writeFloat(pitch);
}
@Override
public void decode(final @NotNull ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
x = byteBuf.readDouble();
y = byteBuf.readDouble();
z = byteBuf.readDouble();
yaw = byteBuf.readFloat();
pitch = byteBuf.readFloat();
}
}
@@ -22,9 +22,14 @@ import xyz.jonesdev.sonar.api.Sonar;
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketDecoder;
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.AnimationPacket;
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.EntityAnimationPacket;
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.SetHeldItemPacket;
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.TransactionPacket;
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.PLAYER_ENTITY_ID;
public final class FallbackProtocolSessionHandler extends FallbackSessionHandler {
public FallbackProtocolSessionHandler(final @NotNull FallbackUser user,
@@ -39,6 +44,7 @@ public final class FallbackProtocolSessionHandler extends FallbackSessionHandler
}
private final boolean forceCAPTCHA;
private boolean waitingSwingArm, waitingSlotConfirm;
private short expectedTransactionId;
private int currentClientSlotId, expectedSlotId = -1;
@@ -50,7 +56,7 @@ public final class FallbackProtocolSessionHandler extends FallbackSessionHandler
*/
private void sendTransaction() {
// Send a Transaction (Ping) packet with a random ID
expectedTransactionId = (short) -(RANDOM.nextInt(Short.MAX_VALUE));
expectedTransactionId = (short) -RANDOM.nextInt(Short.MAX_VALUE);
user.write(new TransactionPacket(0, expectedTransactionId, false));
}
@@ -72,6 +78,17 @@ public final class FallbackProtocolSessionHandler extends FallbackSessionHandler
user.getChannel().flush();
}
/**
* Uses EntityAnimation packets to check for the excepted Animation (SwingArm) from the client.
* <br>
* <a href="https://wiki.vg/Protocol#Entity_Animation">Wiki.vg - EntityAnimation</a>
* <a href="https://wiki.vg/Protocol#Swing_Arm">Wiki.vg - SwingArm</a>
*/
private void sendArmAnimation() {
waitingSwingArm = true;
user.write(new EntityAnimationPacket(PLAYER_ENTITY_ID, EntityAnimationPacket.Type.SWING_MAIN_ARM));
}
private void markSuccess() {
// Either send the player to the vehicle check,
// send the player to the CAPTCHA, or finish the verification.
@@ -108,9 +125,20 @@ public final class FallbackProtocolSessionHandler extends FallbackSessionHandler
// https://wiki.vg/Bedrock_Protocol#Player_Hotbar
if (user.isGeyser()) {
markSuccess();
} else if (waitingSlotConfirm) {
waitingSlotConfirm = false;
expectedSlotId = -1;
// The player did not send duplicate packets, so they pass this check
if (user.isGeyser()) {
markSuccess();
} else {
sendArmAnimation();
}
} else {
sendSetHeldItem();
}
expectedTransactionId = 0;
} else if (packet instanceof SetHeldItemPacket) {
final SetHeldItemPacket heldItemPacket = (SetHeldItemPacket) packet;
@@ -125,11 +153,33 @@ public final class FallbackProtocolSessionHandler extends FallbackSessionHandler
if (expectedSlotId != -1
// Check if the slot ID matches the expected slot ID
// This can false flag if a player spams these packets, which is why we don't fail for this
&& slotId == expectedSlotId) {
markSuccess();
&& slotId == expectedSlotId
// Make sure we actually want to send a transaction at this point in time
&& !waitingSlotConfirm) {
sendTransaction();
waitingSlotConfirm = true;
}
currentClientSlotId = slotId;
} else if (packet instanceof AnimationPacket) {
// Make sure we are awaiting an AnimationPacket packet
if (waitingSwingArm) {
final AnimationPacket animationPacket = (AnimationPacket) packet;
checkState(animationPacket.getHand() == AnimationPacket.MAIN_HAND,
"invalid hand " + animationPacket.getHand());
// Check that the 1.7 client is responding to the correct entity id and animation type.
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) {
// Check entity id is player itself and if the player is sending the correct animation type
if (animationPacket.getEntityId() == PLAYER_ENTITY_ID
&& animationPacket.getType() == AnimationPacket.LegacyAnimationType.SWING_ARM) {
markSuccess();
waitingSwingArm = false;
}
} else {
markSuccess();
waitingSwingArm = false;
}
}
}
}
}
@@ -59,15 +59,17 @@ public final class FallbackVehicleSessionHandler extends FallbackSessionHandler
this.forceCAPTCHA = forceCAPTCHA;
// Send the necessary packets to mount the player on the vehicle
user.delayedWrite(spawnEntity);
user.delayedWrite(setPassengers);
// Send the necessary packets to mount the player on the boat vehicle
user.delayedWrite(spawnBoatEntity);
user.delayedWrite(setBoatPassengers);
user.getChannel().flush();
}
private final boolean forceCAPTCHA;
private int paddlePackets, inputPackets, positionPackets, rotationPackets;
private boolean expectMovement;
private int expectedTransactionId;
private int paddlePackets, inputPackets, positionPackets, rotationPackets, vehicleMovePackets;
private boolean expectMovement, inMinecart, waitingSpawnMinecartTransaction;
private @NotNull FallbackPacket removeEntitiesPacket = removeBoatEntities;
private void markSuccess() {
// Pass the player to the next best verification handler
@@ -91,7 +93,11 @@ public final class FallbackVehicleSessionHandler extends FallbackSessionHandler
checkState(y <= IN_AIR_Y_POSITION, "invalid y position");
// Mark this check as successful if the player sent a few position packets
if (positionPackets++ > Sonar.get().getConfig().getVerification().getVehicle().getMinimumPackets()) {
markSuccess();
if (user.isGeyser() || inMinecart) {
markSuccess();
} else {
spawnMinecart();
}
}
}
@@ -100,20 +106,51 @@ public final class FallbackVehicleSessionHandler extends FallbackSessionHandler
final int minimumPackets = Sonar.get().getConfig().getVerification().getVehicle().getMinimumPackets();
if (paddlePackets > minimumPackets
&& inputPackets > minimumPackets
&& rotationPackets > minimumPackets) {
&& rotationPackets > minimumPackets
&& vehicleMovePackets > minimumPackets) {
// Remove the entity
// The next y coordinate the player will send is going
// to be the vehicle spawn position (y 64 in this case).
user.write(removeEntities);
user.write(removeEntitiesPacket);
// Listen for next movement packet(s)
expectMovement = true;
}
rotationPackets++;
}
private void spawnMinecart() {
expectMovement = false;
paddlePackets = inputPackets = positionPackets = rotationPackets = 0;
waitingSpawnMinecartTransaction = true;
expectedTransactionId = (short) -RANDOM.nextInt(Short.MAX_VALUE);
// Use transaction packets to confirm that the entity has spawned
user.delayedWrite(new TransactionPacket(0, expectedTransactionId, false));
user.delayedWrite(spawnMinecartEntity);
user.delayedWrite(setMinecartPassengers);
user.getChannel().flush();
}
@Override
public void handle(final @NotNull FallbackPacket packet) {
if (packet instanceof SetPlayerPositionRotationPacket) {
if (packet instanceof TransactionPacket) {
// Waiting for transaction to make sure the player is riding on the minecart and not on the boat
if (waitingSpawnMinecartTransaction) {
final TransactionPacket transaction = (TransactionPacket) packet;
// Make sure random transactions aren't counted
checkState(expectedTransactionId <= 0, "unexpected transaction");
// Make sure the transaction was accepted
// This must - by vanilla protocol - always be accepted
checkState(transaction.isAccepted(), "didn't accept transaction");
// Also check if the transaction ID matches the expected ID
checkState(expectedTransactionId == transaction.getTransactionId(),
"expected T ID " + expectedTransactionId + ", but got " + transaction.getTransactionId());
waitingSpawnMinecartTransaction = false;
expectedTransactionId = 0;
removeEntitiesPacket = removeMinecartEntities;
inMinecart = true;
}
} else if (packet instanceof SetPlayerPositionRotationPacket) {
if (expectMovement) {
final SetPlayerPositionRotationPacket posRot = (SetPlayerPositionRotationPacket) packet;
handleMovement(posRot.getY());
@@ -128,7 +165,11 @@ public final class FallbackVehicleSessionHandler extends FallbackSessionHandler
handleRotation();
}
} else if (packet instanceof PaddleBoatPacket) {
checkState(!inMinecart, "invalid packet order (unexpected PaddleBoatPacket)");
paddlePackets++;
} else if (packet instanceof VehicleMovePacket) {
checkState(!inMinecart, "invalid packet order (unexpected VehicleMovePacket)");
vehicleMovePackets++;
} else if (packet instanceof PlayerInputPacket && !expectMovement) {
final PlayerInputPacket playerInput = (PlayerInputPacket) packet;
@@ -153,13 +194,16 @@ public final class FallbackVehicleSessionHandler extends FallbackSessionHandler
"illegal packet order; r/i " + rotationPackets + "/" + inputPackets);
}
// 1.8 and below do not have PaddleBoat packets,
// so we simply exempt them from the PaddleBoat check.
if (user.getProtocolVersion().compareTo(MINECRAFT_1_9) < 0) {
// 1.8 and below do not have PaddleBoat packets, so we simply exempt them from the PaddleBoat check.
// Clients don't send PaddleBoat & VehicleMovePacket packets while riding minecarts.
if (user.getProtocolVersion().compareTo(MINECRAFT_1_9) < 0 || inMinecart) {
paddlePackets++;
vehicleMovePackets++;
} else {
checkState(paddlePackets >= inputPackets,
"illegal packet order; i/p " + inputPackets + "/" + paddlePackets);
checkState(vehicleMovePackets == inputPackets,
"illegal packet order; i/v " + inputPackets + "/" + vehicleMovePackets);
}
inputPackets++;
}