Skip to content

Commit bfba1e2

Browse files
authored
Merge branch 'GrimAnticheat:2.0' into 2.0
2 parents a33a2fe + 86cebe2 commit bfba1e2

File tree

21 files changed

+230
-16
lines changed

21 files changed

+230
-16
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# GrimAC
22

3-
This project is considered feature complete for the 2.0 (open-source) branch of this project. If you would like a bugfix or enhancement and cannot sponsor the work, pull requests are welcome. You can join the [discord](https://discord.gg/SEywtQMt29) for jar releases & changelogs.
3+
This project is considered feature complete for the 2.0 (open-source) branch of this project. If you would like a bugfix or enhancement and cannot sponsor the work, pull requests are welcome. You can join the [discord](https://discord.com/invite/kqQAhTmkUF) for jar releases & changelogs.
44

55
GrimAC is an open source Minecraft anticheat designed for 1.20 and supports 1.8-1.20. It is free while in beta. It will eventually become paid and/or will include offering additional subscription based paid checks. Geyser players are fully exempt.
66

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ plugins {
1010

1111

1212
group = "ac.grim.grimac"
13-
version = "2.3.63"
13+
version = "2.3.64"
1414
description = "Libre simulation anticheat designed for 1.20 with 1.8-1.20 support, powered by PacketEvents 2.0."
1515
java.sourceCompatibility = JavaVersion.VERSION_1_8
1616
java.targetCompatibility = JavaVersion.VERSION_1_8
@@ -41,7 +41,7 @@ dependencies {
4141

4242
implementation("org.jetbrains:annotations:24.1.0")
4343
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
44-
compileOnly("org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT")
44+
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
4545
compileOnly("com.viaversion:viaversion-api:4.9.4-SNAPSHOT")
4646
//
4747
compileOnly("io.netty:netty-all:4.1.85.Final")
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package ac.grim.grimac.checks.impl.badpackets;
2+
3+
import ac.grim.grimac.checks.Check;
4+
import ac.grim.grimac.checks.CheckData;
5+
import ac.grim.grimac.checks.type.PacketCheck;
6+
import ac.grim.grimac.player.GrimPlayer;
7+
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
8+
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
9+
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
10+
import com.github.retrooper.packetevents.util.Vector3i;
11+
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
12+
13+
import static ac.grim.grimac.events.packets.patch.ResyncWorldUtil.resyncPosition;
14+
import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
15+
16+
@CheckData(name = "BadPacketsZ", experimental = true)
17+
public class BadPacketsZ extends Check implements PacketCheck {
18+
public BadPacketsZ(final GrimPlayer player) {
19+
super(player);
20+
}
21+
22+
private boolean exemptNextFinish = false;
23+
private Vector3i lastBlock, lastLastBlock = null;
24+
private final int exemptedY = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 4095 : 255;
25+
26+
// The client sometimes sends a wierd cancel packet
27+
private boolean shouldExempt(final Vector3i pos) {
28+
// lastLastBlock is always null when this happens, and lastBlock isn't
29+
if (lastLastBlock != null || lastBlock == null)
30+
return false;
31+
32+
// on pre 1.14.4 clients, the YPos of this packet is always the same
33+
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) && pos.y != exemptedY)
34+
return false;
35+
36+
// the client only sends this packet if the last block was an instant break
37+
if (getBlockDamage(player, lastBlock) < 1)
38+
return false;
39+
40+
// and if this block is not an instant break
41+
return player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || getBlockDamage(player, pos) < 1;
42+
}
43+
44+
private String formatted(Vector3i vec) {
45+
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
46+
}
47+
48+
public void handle(PacketReceiveEvent event, WrapperPlayClientPlayerDigging dig) {
49+
if (dig.getAction() == DiggingAction.START_DIGGING) {
50+
lastLastBlock = lastBlock;
51+
lastBlock = dig.getBlockPosition();
52+
53+
exemptNextFinish = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14_4) && getBlockDamage(player, lastBlock) >= 1;
54+
return;
55+
}
56+
57+
if (dig.getAction() == DiggingAction.CANCELLED_DIGGING) {
58+
if (shouldExempt(dig.getBlockPosition())) {
59+
lastLastBlock = null;
60+
lastBlock = null;
61+
return;
62+
}
63+
64+
exemptNextFinish = false;
65+
66+
if ((lastBlock == null || !lastBlock.equals(dig.getBlockPosition())) && (lastLastBlock == null || !lastLastBlock.equals(dig.getBlockPosition()))) {
67+
if (flagAndAlert("action=CANCELLED_DIGGING, last=" + formatted(lastBlock) + "/" + formatted(lastLastBlock) + ", pos=" + formatted(dig.getBlockPosition()))) {
68+
if (shouldModifyPackets()) {
69+
event.setCancelled(true);
70+
player.onPacketCancel();
71+
}
72+
}
73+
}
74+
75+
lastLastBlock = null;
76+
lastBlock = null;
77+
}
78+
79+
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
80+
if (exemptNextFinish) {
81+
exemptNextFinish = false;
82+
return;
83+
}
84+
85+
if ((lastBlock == null || !lastBlock.equals(dig.getBlockPosition())) && (lastLastBlock == null || !lastLastBlock.equals(dig.getBlockPosition()))) {
86+
if (flagAndAlert("action=FINISHED_DIGGING, last=" + formatted(lastBlock) + "/" + formatted(lastLastBlock) + ", pos=" + formatted(dig.getBlockPosition()))) {
87+
if (shouldModifyPackets()) {
88+
event.setCancelled(true);
89+
player.onPacketCancel();
90+
resyncPosition(player, dig.getBlockPosition());
91+
}
92+
}
93+
}
94+
95+
// 1.14.4+ clients don't send another start break in protected regions
96+
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) {
97+
lastLastBlock = null;
98+
lastBlock = null;
99+
}
100+
}
101+
}
102+
}

src/main/java/ac/grim/grimac/commands/GrimLog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public void onLog(CommandSender sender, int flagId) {
3030
} else {
3131
String uploading = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("upload-log-start", "%prefix% &fUploading log... please wait");
3232
String success = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("upload-log", "%prefix% &fUploaded debug to: %url%");
33-
String failure = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("upload-log-upload-failure", "%prefix% &cSomething went wrong while uploading this log, see console for more info");
33+
String failure = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("upload-log-upload-failure", "%prefix% &cSomething went wrong while uploading this log, see console for more information.");
3434

3535
sender.sendMessage(MessageUtil.format(uploading));
3636

src/main/java/ac/grim/grimac/commands/GrimProfile.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void onConsoleDebug(CommandSender sender, OnlinePlayer target) {
2929

3030
// Short circuit due to minimum java requirements for MultiLib
3131
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_18) && MultiLibUtil.isExternalPlayer(target.getPlayer())) {
32-
String alertString = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("player-not-this-server", "%prefix% &cPlayer isn't on this server!");
32+
String alertString = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("player-not-this-server", "%prefix% &cThis player isn't on this server!");
3333
sender.sendMessage(MessageUtil.format(alertString));
3434
return;
3535
}

src/main/java/ac/grim/grimac/commands/GrimSpectate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void onSpectate(CommandSender sender, @Optional OnlinePlayer target) {
3333
}
3434

3535
if (target == null || (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_18) && MultiLibUtil.isExternalPlayer(target.getPlayer()))) {
36-
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("player-not-this-server", "%prefix% &cPlayer isn't on this server!");
36+
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("player-not-this-server", "%prefix% &cThis player isn't on this server!");
3737
sender.sendMessage(MessageUtil.format(message));
3838
return;
3939
}

src/main/java/ac/grim/grimac/commands/GrimStopSpectating.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void onStopSpectate(CommandSender sender, String[] args) {
2323
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
2424
GrimAPI.INSTANCE.getSpectateManager().disable(player, teleportBack);
2525
} else {
26-
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-spectate-return", "%prefix% &cYou can only do this after spectating a player");
26+
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-spectate-return", "%prefix% &cYou can only do this after spectating a player.");
2727
sender.sendMessage(MessageUtil.format(message));
2828
}
2929
}

src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import ac.grim.grimac.GrimAPI;
44
import ac.grim.grimac.checks.impl.badpackets.BadPacketsX;
5+
import ac.grim.grimac.checks.impl.badpackets.BadPacketsZ;
56
import ac.grim.grimac.events.packets.patch.ResyncWorldUtil;
67
import ac.grim.grimac.player.GrimPlayer;
78
import ac.grim.grimac.utils.anticheat.update.*;
@@ -321,7 +322,9 @@ private static void handleBlockPlaceOrUseItem(PacketWrapper packet, GrimPlayer p
321322
}
322323

323324
private boolean isMojangStupid(GrimPlayer player, WrapperPlayClientPlayerFlying flying) {
324-
double threshold = player.getMovementThreshold();
325+
final Location location = flying.getLocation();
326+
final double threshold = player.getMovementThreshold();
327+
325328
// Don't check duplicate 1.17 packets (Why would you do this mojang?)
326329
// Don't check rotation since it changes between these packets, with the second being irrelevant.
327330
//
@@ -333,21 +336,34 @@ private boolean isMojangStupid(GrimPlayer player, WrapperPlayClientPlayerFlying
333336
// Mojang added this stupid mechanic in 1.17
334337
&& (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_17) &&
335338
// Due to 0.03, we can't check exact position, only within 0.03
336-
player.filterMojangStupidityOnMojangStupidity.distanceSquared(flying.getLocation().getPosition()) < threshold * threshold))
339+
player.filterMojangStupidityOnMojangStupidity.distanceSquared(location.getPosition()) < threshold * threshold))
337340
// If the player was in a vehicle, has position and look, and wasn't a teleport, then it was this stupid packet
338341
|| player.compensatedEntities.getSelf().inVehicle())) {
342+
343+
// Mark that we want this packet to be cancelled from reaching the server
344+
// Additionally, only yaw/pitch matters: https://github.com/GrimAnticheat/Grim/issues/1275#issuecomment-1872444018
345+
// 1.9+ isn't impacted by this packet as much.
346+
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_9)) {
347+
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("cancel-duplicate-packet", true)) {
348+
player.packetStateData.cancelDuplicatePacket = true;
349+
}
350+
} else {
351+
// Override location to force it to use the last real position of the player. Prevents position-related bypasses like nofall.
352+
flying.setLocation(new Location(player.filterMojangStupidityOnMojangStupidity.getX(), player.filterMojangStupidityOnMojangStupidity.getY(), player.filterMojangStupidityOnMojangStupidity.getZ(), location.getYaw(), location.getPitch()));
353+
}
354+
339355
player.packetStateData.lastPacketWasOnePointSeventeenDuplicate = true;
340356

341-
if (player.xRot != flying.getLocation().getYaw() || player.yRot != flying.getLocation().getPitch()) {
357+
if (player.xRot != location.getYaw() || player.yRot != location.getPitch()) {
342358
player.lastXRot = player.xRot;
343359
player.lastYRot = player.yRot;
344360
}
345361

346362
// Take the pitch and yaw, just in case we were wrong about this being a stupidity packet
347-
player.xRot = flying.getLocation().getYaw();
348-
player.yRot = flying.getLocation().getPitch();
363+
player.xRot = location.getYaw();
364+
player.yRot = location.getPitch();
349365

350-
player.packetStateData.lastClaimedPosition = flying.getLocation().getPosition();
366+
player.packetStateData.lastClaimedPosition = location.getPosition();
351367
return true;
352368
}
353369
return false;
@@ -399,6 +415,7 @@ public void onPacketReceive(PacketReceiveEvent event) {
399415

400416
// The player flagged crasher or timer checks, therefore we must protect predictions against these attacks
401417
if (event.isCancelled() && (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) || event.getPacketType() == PacketType.Play.Client.VEHICLE_MOVE)) {
418+
player.packetStateData.cancelDuplicatePacket = false;
402419
return;
403420
}
404421

@@ -435,6 +452,7 @@ public void onPacketReceive(PacketReceiveEvent event) {
435452
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(dig.getBlockPosition());
436453

437454
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, block.getType());
455+
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
438456

439457
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
440458
// Not unbreakable
@@ -546,6 +564,11 @@ public void onPacketReceive(PacketReceiveEvent event) {
546564
// Such as the NoFall check setting the player to not be on the ground
547565
player.checkManager.onPacketReceive(event);
548566

567+
if (player.packetStateData.cancelDuplicatePacket) {
568+
event.setCancelled(true);
569+
player.packetStateData.cancelDuplicatePacket = false;
570+
}
571+
549572
// Finally, remove the packet state variables on this packet
550573
player.packetStateData.lastPacketWasOnePointSeventeenDuplicate = false;
551574
player.packetStateData.lastPacketWasTeleport = false;

src/main/java/ac/grim/grimac/events/packets/PacketPlayerRespawn.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,39 @@
2424
import java.util.List;
2525
import java.util.Objects;
2626

27+
/**
28+
* PlayerRespawnS2CPacket info (1.20.2+):
29+
* If the world is different (check via registry key), world is recreated (all entities etc destroyed).
30+
* <p>
31+
* Client player is ALWAYS recreated
32+
* <p>
33+
* If the packet has the `KEEP_TRACKED_DATA` flag:
34+
* Sneaking and Sprinting fields are kept on the new client player.
35+
* <p>
36+
* If the packet has the `KEEP_ATTRIBUTES` flag:
37+
* Attributes are kept.
38+
* <p>
39+
* New client player is initialised:
40+
* Pose is set to standing.
41+
* Velocity is set to zero.
42+
* Pitch is set to 0.
43+
* Yaw is set to -180.
44+
*/
45+
// TODO update for 1.20.2-
2746
public class PacketPlayerRespawn extends PacketListenerAbstract {
2847

2948
public PacketPlayerRespawn() {
3049
super(PacketListenerPriority.HIGH);
3150
}
3251

52+
private static final byte KEEP_ATTRIBUTES = 1;
53+
private static final byte KEEP_TRACKED_DATA = 2;
54+
private static final byte KEEP_ALL = 3;
55+
56+
private boolean hasFlag(WrapperPlayServerRespawn respawn, byte flag) {
57+
return (respawn.getKeptData() & flag) != 0;
58+
}
59+
3360
@Override
3461
public void onPacketSend(PacketSendEvent event) {
3562
if (event.getPacketType() == PacketType.Play.Server.UPDATE_HEALTH) {
@@ -101,11 +128,17 @@ public void onPacketSend(PacketSendEvent event) {
101128
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get() + 1, () -> {
102129
player.isSneaking = false;
103130
player.lastOnGround = false;
131+
player.onGround = false;
104132
player.isInBed = false;
105133
player.packetStateData.packetPlayerOnGround = false; // If somewhere else pulls last ground to fix other issues
106134
player.packetStateData.lastClaimedPosition = new Vector3d();
107135
player.filterMojangStupidityOnMojangStupidity = new Vector3d();
108-
player.lastSprintingForSpeed = false; // This is reverted even on 1.18 clients
136+
137+
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19_4)) {
138+
player.isSprinting = !this.hasFlag(respawn, KEEP_TRACKED_DATA);
139+
} else {
140+
player.lastSprintingForSpeed = false;
141+
}
109142

110143
player.checkManager.getPacketCheck(BadPacketsE.class).handleRespawn(); // Reminder ticks reset
111144

@@ -141,6 +174,20 @@ public void onPacketSend(PacketSendEvent event) {
141174
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
142175
player.compensatedWorld.setDimension(respawn.getDimension(), event.getUser());
143176
}
177+
178+
// TODO this needs to be done for other client versions as well. And there should probably be some attribute holder that we can just call reset() on.
179+
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20_5) && !this.hasFlag(respawn, KEEP_ATTRIBUTES)) {
180+
// Reset attributes if not kept
181+
final PacketEntitySelf self = player.compensatedEntities.getSelf();
182+
self.gravityAttribute = 0.08d;
183+
self.stepHeight = 0.6f;
184+
self.scale = 1.0f;
185+
self.setJumpStrength(0.42f);
186+
self.setBreakSpeedMultiplier(1.0f);
187+
self.setBlockInteractRange(4.5);
188+
self.setEntityInteractRange(3.0);
189+
player.compensatedEntities.hasSprintingAttributeEnabled = false;
190+
}
144191
});
145192
}
146193
}

src/main/java/ac/grim/grimac/manager/CheckManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public CheckManager(GrimPlayer player) {
9292
.put(BadPacketsW.class, new BadPacketsW(player))
9393
.put(BadPacketsX.class, new BadPacketsX(player))
9494
.put(BadPacketsY.class, new BadPacketsY(player))
95+
.put(BadPacketsZ.class, new BadPacketsZ(player))
9596
.put(FastBreak.class, new FastBreak(player))
9697
.put(TransactionOrder.class, new TransactionOrder(player))
9798
.put(NoSlowB.class, new NoSlowB(player))

0 commit comments

Comments
 (0)