Skip to content

Commit

Permalink
Merge branch 'merge-dev' into merge
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/main/java/ac/grim/grimac/commands/GrimDump.java
#	src/main/java/ac/grim/grimac/commands/GrimLog.java
#	src/main/java/ac/grim/grimac/commands/GrimVersion.java
#	src/main/java/ac/grim/grimac/manager/init/start/PlaceholderAPIExpansion.java
#	src/main/java/ac/grim/grimac/utils/nmsutil/BlockRayTrace.java
  • Loading branch information
Axionize committed Jan 3, 2025
2 parents ad68a43 + 0adc332 commit 3f1b4d8
Show file tree
Hide file tree
Showing 113 changed files with 1,175 additions and 721 deletions.
7 changes: 1 addition & 6 deletions src/main/java/ac/grim/grimac/checks/Check.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ public class Check extends GrimProcessor implements AbstractCheck {
private boolean isEnabled;
private boolean exempted;

@Override
public boolean isExperimental() {
return experimental;
}

public Check(final GrimPlayer player) {
this.player = player;

Expand All @@ -57,7 +52,7 @@ public Check(final GrimPlayer player) {
this.description = checkData.description();
this.displayName = this.checkName;
}
//

reload();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.packetorder;
package ac.grim.grimac.checks.impl.badpackets;

import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
Expand All @@ -11,15 +11,15 @@
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;

@CheckData(name = "PacketOrderB")
public class PacketOrderB extends Check implements PacketCheck {
@CheckData(name = "BadPacketsH", description = "Did not swing for attack")
public class BadPacketsH extends Check implements PacketCheck {

// 1.9 packet order: INTERACT -> ANIMATION
// 1.8 packet order: ANIMATION -> INTERACT
// I personally think 1.8 made much more sense. You swing and THEN you hit!
private boolean sentAnimation = player.getClientVersion().isNewerThan(ClientVersion.V_1_8);

public PacketOrderB(final GrimPlayer player) {
public BadPacketsH(final GrimPlayer player) {
super(player);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ac.grim.grimac.checks.impl.badpackets;

import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.PacketCheck;
import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;

@CheckData(name = "BadPacketsV", description = "Did not move far enough", experimental = true)
public class BadPacketsV extends Check implements PacketCheck {
public BadPacketsV(GrimPlayer player) {
super(player);
}

private int noReminderTicks;

@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (!player.canSkipTicks() && isTickPacket(event.getPacketType())) {
if (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION || event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION) {
if (noReminderTicks < 20 && !player.uncertaintyHandler.lastTeleportTicks.hasOccurredSince(1)) {
final double deltaSq = new WrapperPlayClientPlayerFlying(event).getLocation().getPosition()
.distanceSquared(new Vector3d(player.lastX, player.lastY, player.lastZ));
if (deltaSq <= player.getMovementThreshold() * player.getMovementThreshold()) {
flagAndAlert("delta=" + Math.sqrt(deltaSq));
}
}

noReminderTicks = 0;
} else {
noReminderTicks++;
}
}
}
}
128 changes: 58 additions & 70 deletions src/main/java/ac/grim/grimac/checks/impl/badpackets/BadPacketsX.java
Original file line number Diff line number Diff line change
@@ -1,93 +1,81 @@
package ac.grim.grimac.checks.impl.badpackets;

import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.checks.type.PostPredictionCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.change.BlockModification;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@CheckData(name = "BadPacketsX")
public class BadPacketsX extends Check implements BlockBreakCheck {
@CheckData(name = "BadPacketsX", experimental = true)
public class BadPacketsX extends Check implements PostPredictionCheck {
public BadPacketsX(GrimPlayer player) {
super(player);
}

private int lastTick;
private boolean didLastFlag;
private Vector3i lastBreakLoc;
private StateType lastBlockType;

public final boolean noFireHitbox = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_15_2);
private boolean sprint;
private boolean sneak;
private int flags;

@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action != DiggingAction.START_DIGGING && blockBreak.action != DiggingAction.FINISHED_DIGGING)
public void onPredictionComplete(final PredictionComplete predictionComplete) {
if (!player.canSkipTicks()) {
if (flags > 0) {
setbackIfAboveSetbackVL();
}

flags = 0;
return;
}

final StateType block = blockBreak.block.getType();
if (player.isTickingReliablyFor(3)) {
for (; flags > 0; flags--) {
if (flagAndAlert()) {
setbackIfAboveSetbackVL();
}
}
}

// Fixes false from breaking kelp underwater
// The client sends two start digging packets to the server both in the same tick. BadPacketsX gets called twice, doesn't false the first time, but falses the second
// One ends up breaking the kelp, the other ends up doing nothing besides falsing this check because we think they're trying to mine water
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
if (lastTick == newTick
&& lastBreakLoc.equals(blockBreak.position)
&& !didLastFlag
&& lastBlockType.getHardness() == 0.0F
&& lastBlockType.getBlastResistance() == 0.0F
&& block == StateTypes.WATER
) return;
flags = 0;
}

// prevents rare false on rapidly breaking short grass
List<StateType> previousBlockStates = player.blockHistory.modificationQueue.stream()
.filter((blockModification) -> blockModification.getLocation().equals(blockBreak.position)
&& newTick - blockModification.getTick() < 2
&& (blockModification.getCause() == BlockModification.Cause.START_DIGGING || blockModification.getCause() == BlockModification.Cause.HANDLE_NETTY_SYNC_TRANSACTION))
.flatMap(mod -> Stream.of(mod.getOldBlockContents().getType()))
.collect(Collectors.toList());
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (player.gamemode == GameMode.SPECTATOR || isTickPacket(event.getPacketType())) {
sprint = sneak = false;
return;
}

previousBlockStates.add(0, block);
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
WrapperPlayClientEntityAction wrapper = new WrapperPlayClientEntityAction(event);
switch (wrapper.getAction()) {
case START_SNEAKING:
case STOP_SNEAKING:
if (sneak) {
if (player.canSkipTicks() || flagAndAlert()) {
flags++;
}
}
sneak = true;
break;

boolean invalid = false;
for (StateType possibleBlockState : previousBlockStates) {
// the block does not have a hitbox
invalid = (possibleBlockState == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
|| possibleBlockState.isAir()
|| possibleBlockState == StateTypes.WATER
|| possibleBlockState == StateTypes.LAVA
|| possibleBlockState == StateTypes.BUBBLE_COLUMN
|| possibleBlockState == StateTypes.MOVING_PISTON
|| possibleBlockState == StateTypes.FIRE && noFireHitbox
// or the client claims to have broken an unbreakable block
|| possibleBlockState.getHardness() == -1.0f && blockBreak.action == DiggingAction.FINISHED_DIGGING;
if (!invalid) {
break;
}
}
case START_SPRINTING:
case STOP_SPRINTING:
if (sprint) {
if (player.canSkipTicks() || flagAndAlert()) {
flags++;
}
}
sprint = true;
break;

if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + blockBreak.action)) {
didLastFlag = true;
if (shouldModifyPackets()) {
blockBreak.cancel();
default:
// Handle other cases if necessary
break;
}
} else {
didLastFlag = false;
}
lastTick = newTick;
lastBreakLoc = blockBreak.position;
lastBlockType = block;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package ac.grim.grimac.checks.impl.breaking;

import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3i;

@CheckData(name = "AirLiquidBreak", description = "Breaking a block that cannot be broken")
public class AirLiquidBreak extends Check implements BlockBreakCheck {
public AirLiquidBreak(GrimPlayer player) {
super(player);
}

private int lastTick;
private boolean didLastFlag;
private Vector3i lastBreakLoc;
private StateType lastBlockType;

public final boolean noFireHitbox = player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_15_2);

@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.action != DiggingAction.START_DIGGING && blockBreak.action != DiggingAction.FINISHED_DIGGING)
return;

final StateType block = blockBreak.block.getType();

// Fixes false from breaking kelp underwater
// The client sends two start digging packets to the server both in the same tick. AirLiquidBreak gets called twice, doesn't false the first time, but falses the second
// One ends up breaking the kelp, the other ends up doing nothing besides falsing this check because we think they're trying to mine water
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
if (lastTick == newTick
&& lastBreakLoc.equals(blockBreak.position)
&& !didLastFlag
&& lastBlockType.getHardness() == 0.0F
&& lastBlockType.getBlastResistance() == 0.0F
&& block == StateTypes.WATER
) return;
lastTick = newTick;
lastBreakLoc = blockBreak.position;
lastBlockType = block;

// the block does not have a hitbox
boolean invalid = (block == StateTypes.LIGHT && !(player.getInventory().getHeldItem().is(ItemTypes.LIGHT) || player.getInventory().getOffHand().is(ItemTypes.LIGHT)))
|| block.isAir()
|| block == StateTypes.WATER
|| block == StateTypes.LAVA
|| block == StateTypes.BUBBLE_COLUMN
|| block == StateTypes.MOVING_PISTON
|| block == StateTypes.FIRE && noFireHitbox
// or the client claims to have broken an unbreakable block
|| block.getHardness() == -1.0f && blockBreak.action == DiggingAction.FINISHED_DIGGING;

if (invalid && flagAndAlert("block=" + block.getName() + ", type=" + blockBreak.action) && shouldModifyPackets()) {
didLastFlag = true;
blockBreak.cancel();
} else {
didLastFlag = false;
}
}
}
45 changes: 45 additions & 0 deletions src/main/java/ac/grim/grimac/checks/impl/breaking/FarBreak.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ac.grim.grimac.checks.impl.breaking;

import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.math.VectorUtils;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import org.bukkit.util.Vector;

@CheckData(name = "FarBreak", description = "Breaking blocks too far away", experimental = true)
public class FarBreak extends Check implements BlockBreakCheck {
public FarBreak(GrimPlayer player) {
super(player);
}

@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (player.gamemode == GameMode.SPECTATOR || player.compensatedEntities.getSelf().inVehicle() || blockBreak.action == DiggingAction.CANCELLED_DIGGING) return; // falses

double min = Double.MAX_VALUE;
for (double d : player.getPossibleEyeHeights()) {
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
Vector eyes = new Vector(player.x, player.y + d, player.z);
Vector best = VectorUtils.cutBoxToVector(eyes, box);
min = Math.min(min, eyes.distanceSquared(best));
}

// getPickRange() determines this?
// With 1.20.5+ the new attribute determines creative mode reach using a modifier
double maxReach = player.compensatedEntities.getSelf().getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
if (player.packetStateData.didLastMovementIncludePosition || player.canSkipTicks()) {
double threshold = player.getMovementThreshold();
maxReach += Math.hypot(threshold, threshold);
}

if (min > maxReach * maxReach && flagAndAlert(String.format("distance=%.2f", Math.sqrt(min))) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ac.grim.grimac.checks.impl.misc;
package ac.grim.grimac.checks.impl.breaking;

import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ac.grim.grimac.checks.impl.breaking;

import ac.grim.grimac.checks.Check;
import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockBreakCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockBreak;

@CheckData(name = "InvalidBreak", description = "Sent impossible block face id")
public class InvalidBreak extends Check implements BlockBreakCheck {
public InvalidBreak(GrimPlayer player) {
super(player);
}

@Override
public void onBlockBreak(BlockBreak blockBreak) {
if (blockBreak.faceId < 0 || blockBreak.faceId > 5) {
// ban
if (flagAndAlert("face=" + blockBreak.faceId) && shouldModifyPackets()) {
blockBreak.cancel();
}
}
}
}
Loading

0 comments on commit 3f1b4d8

Please sign in to comment.