Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check Line of Sight of Player Before Block Placements and Opening Containers #1679

Open
wants to merge 106 commits into
base: 2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 89 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
b599008
Merge reach wall hit check
Axionize Aug 23, 2024
4e08ec9
Deobfuscate and document purpose of misunderstood code in traverseBlo…
Axionize Aug 23, 2024
e917212
Container LOS initial commit
Axionize Aug 24, 2024
01bd2c0
Stash commits - compensated world
Axionize Aug 24, 2024
ae3ea30
Stash commits - compensated everything
Axionize Aug 24, 2024
8b645b7
Stash commits - loop alternate possible scenarios
Axionize Aug 24, 2024
3c78a63
Workable check
Axionize Aug 24, 2024
6a13c9f
Expand hitbox of target block by 0.03/0.0002 to account for known MC bug
Axionize Aug 24, 2024
328596f
Expand hitbox of target block by 0.03/0.0002 to account for known MC bug
Axionize Aug 24, 2024
1786360
Clean up Code and add code for blockTypeWhitelist to BlockLOS checks …
Axionize Aug 25, 2024
772ba56
Rename check
Axionize Aug 25, 2024
d869ff9
Add config options for check and enable it by adding to punishments
Axionize Aug 25, 2024
177a8b0
Add config options for check and enable it by adding to punishments
Axionize Aug 25, 2024
cccecba
Remove RotationPlace, replacec with LineOfSightPlace
Axionize Aug 25, 2024
a508ec2
0.03/0.0002 changes, cleanup code, add comments
Axionize Aug 25, 2024
bc239a7
Fix block whitelist
Axionize Aug 25, 2024
f78c80f
Temporarily comment out system.out.println
Axionize Aug 25, 2024
2b55268
Fix styling
Axionize Aug 25, 2024
3315e3c
Revert "Merge reach wall hit check"
Axionize Aug 26, 2024
4515d21
Seperate LOS-block-place checks from Sam's LOS reach checks
Axionize Aug 26, 2024
dd7bb81
Rework whitelist to use HashSet
Axionize Aug 26, 2024
d272107
Remove print statements and cleanup comments
Axionize Aug 26, 2024
2898fed
1.9+ tick behind and 1.7 ticking behaviour compensation
Axionize Aug 26, 2024
2a88d3e
Account for player being inside of target block
Axionize Aug 26, 2024
dee3268
Make variables private
Axionize Aug 26, 2024
483f317
Remove unneeded entry in punishments.yml
Axionize Aug 26, 2024
45ba746
Comment cleanup
Axionize Aug 26, 2024
9c2a034
Temporary Hacky Fix to demonstrate cause of fence false flag
Axionize Aug 29, 2024
b2fa497
Update src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
Axionize Aug 29, 2024
8a351ec
Update src/main/java/ac/grim/grimac/utils/collisions/datatypes/Comple…
Axionize Aug 29, 2024
a027355
Update src/main/java/ac/grim/grimac/events/packets/CheckManagerListen…
Axionize Aug 29, 2024
88e439e
Improve temporary fix - delete unncessary code
Axionize Aug 29, 2024
ded614f
MVP commit
Axionize Aug 29, 2024
050c22c
Clean up
Axionize Aug 29, 2024
2177b12
Print out target block when flagged - help find falses from constant …
Axionize Aug 30, 2024
800c79a
Document Hex vs Simple CollisionBox purpose
Axionize Aug 31, 2024
66228f5
Add lever hitboxes and fix banner hitboxes
Axionize Aug 31, 2024
837516e
Fix syntax error
Axionize Aug 31, 2024
3a145cb
Fix banners
Axionize Sep 1, 2024
c0630f9
Crop Collision Box Overhaul
Axionize Aug 26, 2024
94a31f7
Fix Sweet Berry Collision + HitBox
Axionize Sep 1, 2024
c66522c
Move Blocks with NoCollision but a Outline/Hitbox into HitboxData
Axionize Sep 2, 2024
f7529a1
Remove Fence Hack; Fix Lever South Wall Hitbox; Initial Work for Flow…
Axionize Sep 2, 2024
07ad12a
Update src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
Axionize Sep 2, 2024
b4ea451
Update src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java
Axionize Sep 2, 2024
9d192b2
Add OffsetCollisionBox; Document all OffSetBlocks and quirks
Axionize Sep 2, 2024
3fc366b
Fix Tall Flower Hit and Collision boxes
Axionize Sep 2, 2024
d498644
Fix Mangrove Propagule, add HexOffsetCollisionBox
Axionize Sep 2, 2024
86d19de
Reorder HitboxData - Groups Before Individual StateTypes so individua…
Axionize Sep 2, 2024
2f6f9bc
Fix Tall Seagrass hitbox
Axionize Sep 2, 2024
21668a6
Fix Tall Grass & Large Ferns
Axionize Sep 2, 2024
ba0fa7a
Refactor Bamboo to use OffsetCollisionBox; Fix Bamboo Sapling Hit & C…
Axionize Sep 2, 2024
344ba41
Fix Hanging Roots hit/collision box
Axionize Sep 2, 2024
2236efe
Fix Small Dripleaf
Axionize Sep 2, 2024
b8f59fa
Fix Pointed Dripstone
Axionize Sep 2, 2024
4be835d
Skip redstone wires
Axionize Sep 6, 2024
8854b00
Support for buttons in 1.7
Axionize Sep 6, 2024
e23d191
Merge branch '2.0' into los-containers
Axionize Sep 9, 2024
b78a36f
accidentally added bamboo to No Collision
Axionize Sep 9, 2024
1ac0b2e
Fix Bamboo HitBox & Collision Box
Axionize Sep 9, 2024
9cab8f7
Fix flower offset being visual in 1.8 + bamboo sim
Axionize Sep 9, 2024
569a8b4
'Fix' 0.03 for 1.8-1.18.1
Axionize Sep 14, 2024
5793d7b
Merge branch '2.0' into los-containers
Axionize Sep 14, 2024
3afd2c4
Skip post on pre
Axionize Sep 14, 2024
8fe6094
Bypass check when player near moving shulkers, pistons
Axionize Sep 14, 2024
528712e
Fix Wall Banners and Banner Simulation falses
Axionize Sep 15, 2024
5f07079
Also check blockfaces
Axionize Sep 15, 2024
fc58fcc
Fix Wall Banners and Banner Simulation falses
Axionize Sep 15, 2024
6cfda64
Fix flower and short grass offsets for older versions
Axionize Sep 15, 2024
d8c2f77
Pane fix outline
Axionize Sep 15, 2024
ed643f3
legacy pane hitbox faces, still falses because different face?
Axionize Sep 15, 2024
d6d86ef
Fix rails
Axionize Sep 15, 2024
9cba7a9
Fix cactus
Axionize Sep 15, 2024
c560539
Brewing stands (excluding 1.8-1.8.9)
Axionize Sep 15, 2024
b92c872
End portals
Axionize Sep 15, 2024
4593f30
Quick and dirty lever fix
Axionize Sep 15, 2024
f6aeffb
Quick and dirty button patch
Axionize Sep 15, 2024
1e5ad65
Fix null exception
Axionize Sep 15, 2024
5c709c1
Check 0.03/0.002 and non-expanded hitbox (for blockfaces)
Axionize Sep 15, 2024
7215dfc
Add scoring system for most likely blockface
Axionize Sep 15, 2024
7cc3c8f
Fix blockface false flags
Axionize Sep 15, 2024
5d366e7
Fix rails
Axionize Sep 15, 2024
31fa689
Fix modern walls
Axionize Sep 16, 2024
87194d7
Fix legacy walls
Axionize Sep 16, 2024
0852ac8
Fix ascending rails on 1.8.9
Axionize Sep 16, 2024
fc89b93
Cleanup exit conditions
Axionize Sep 16, 2024
4a9d862
Fix 1.8 Brewing Stand bug
Axionize Sep 16, 2024
ace5178
Minor optimizations
Axionize Sep 16, 2024
4cff51c
More minor optimizations
Axionize Sep 16, 2024
61d9609
More minor optimizations
Axionize Sep 16, 2024
1b75734
Update src/main/java/ac/grim/grimac/events/packets/CheckManagerListen…
Axionize Sep 17, 2024
e2f3722
Minor cleanup
Axionize Sep 17, 2024
1355454
Deobfuscate and document purpose of misunderstood code in traverseBlo…
Axionize Sep 17, 2024
05e7b3c
Exit conditions early and remove unnccessary if statement
Axionize Sep 18, 2024
7007722
Fix cactuses for pre-1.13
Axionize Sep 18, 2024
d741459
Fix levers and buttons for < 1.13
Axionize Sep 18, 2024
b18a5be
Fix fences on legacy servers
Axionize Sep 20, 2024
1f49ab2
Update src/main/java/ac/grim/grimac/player/GrimPlayer.java
Axionize Sep 20, 2024
6b5786b
Fix legacy hitboxes for versions up to 1.12.2 and make fence hitbox g…
Axionize Sep 20, 2024
9694b75
Inline lever hitboxes
Axionize Sep 20, 2024
ad6b243
Minor Dynamic Fence cleanup
Axionize Sep 20, 2024
b096e02
Optimize getPossibleEyeHeights()
Axionize Sep 22, 2024
bd6c252
Optimize iteration order
Axionize Sep 22, 2024
07881da
More minor optimizations
Axionize Sep 22, 2024
4efaf8d
Fix Array matching error and block
Axionize Sep 22, 2024
9a5c16f
Merge branch '2.0' into los-containers
Axionize Sep 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package ac.grim.grimac.checks.impl.scaffolding;

import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.events.packets.CheckManagerListener;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.nmsutil.Ray;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
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.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;

import java.util.*;

@CheckData(name = "LineOfSightPlace")
public class LineOfSightPlace extends BlockPlaceCheck {

private double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
private boolean ignorePost = false;
private boolean useBlockWhitelist;
private HashSet<StateType> blockWhitelist;
Axionize marked this conversation as resolved.
Show resolved Hide resolved

public LineOfSightPlace(GrimPlayer player) {
super(player);
}

@Override
public void onBlockPlace(final BlockPlace place) {
if (checkIfShouldSkip(place)) return;

if (flagBuffer > 0 && !didRayTraceHit(place)) {
ignorePost = true;
// If the player hit and has flagged this check recently
if (flagAndAlert("pre-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType()) && shouldModifyPackets() && shouldCancel()) {
place.resync(); // Deny the block placement.
}
}
}

// Use post flying because it has the correct rotation, and can't false easily.
@Override
public void onPostFlyingBlockPlace(BlockPlace place) {
if (checkIfShouldSkip(place)) return;

// Don't flag twice
if (ignorePost) {
ignorePost = false;
return;
}

// Ray trace to try and hit the target block.
boolean hit = didRayTraceHit(place);
// This can false with rapidly moving yaw in 1.8+ clients
if (!hit) {
flagBuffer = 1;
flagAndAlert("post-flying: " + player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType());
} else {
flagBuffer = Math.max(0, flagBuffer - 0.1);
}
}

private boolean checkIfShouldSkip(BlockPlace place) {
StateType targetBlockStateType = player.compensatedWorld.getWrappedBlockStateAt(place.getPlacedAgainstBlockLocation()).getType();
if (place.getMaterial() == StateTypes.SCAFFOLDING) return true; // Scaffolding
if (player.gamemode == GameMode.SPECTATOR) return true; // A waste to check creative mode players
if (targetBlockStateType == StateTypes.REDSTONE_WIRE) return true; // Redstone too buggy
if (player.compensatedWorld.isNearHardEntity(player.boundingBox.copy().expand(4))) return true; // Shulkers and Pistons are too buggy

if (useBlockWhitelist) {
if (!isBlockTypeWhitelisted(targetBlockStateType)) {
return true;
}
}
return false;
}

private boolean didRayTraceHit(BlockPlace place) {
double[] possibleEyeHeights = player.getPossibleEyeHeightsArray();

// Start checking if player is in the block
double minEyeHeight = Double.MAX_VALUE;
double maxEyeHeight = Double.MIN_VALUE;
for (double height : possibleEyeHeights) {
minEyeHeight = Math.min(minEyeHeight, height);
maxEyeHeight = Math.max(maxEyeHeight, height);
}

double movementThreshold = player.getMovementThreshold();

SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
eyePositions.expand(movementThreshold);

Vector3i interactBlockVec = place.getPlacedAgainstBlockLocation();
BlockFace expectedBlockFace = place.getDirection();

// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
// This may potentially be exploitable as a minor bypass
if (eyePositions.isIntersected(new SimpleCollisionBox(interactBlockVec))) {
return true;
}
// End checking if the player is in the block
double[][] possibleLookDirs;

// 1.9+ players could be a tick behind because we don't get skipped ticks
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
possibleLookDirs = new double[][]{
{player.lastXRot, player.yRot, 0},
{player.xRot, player.yRot, 0},
{player.lastXRot, player.lastYRot, 0}
};
} else if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
// 1.7 players do not have any of these issues! They are always on the latest look vector
possibleLookDirs = new double[][]{{player.xRot, player.yRot, 0}};
} else {
possibleLookDirs = new double[][]{
{player.lastXRot, player.yRot, 0},
{player.xRot, player.yRot, 0}
};
}

// We do not need to add 0.03/0.0002 to maxDistance to ensure our raytrace hits blocks
// Since we expand the hitboxes of the expectedTargetBlock by 0.03/0.002 already later
double maxDistance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);

// Define possible offsets
// TODO, vectorize this with SIMD or AVX for performance
double[][] offsets = {
{0, 0, 0},
{movementThreshold, 0, 0},
{-movementThreshold, 0, 0},
{0, movementThreshold, 0},
{0, -movementThreshold, 0},
{0, 0, movementThreshold},
{0, 0, -movementThreshold}
};

double[] eyePosition = new double[3];
double[] eyeLookDir = new double[3];

for (double eyeHeight : possibleEyeHeights) {
for (double[] lookDir : possibleLookDirs) {
for (double[] offset : offsets) {
eyePosition[0] = player.x + offset[0];
eyePosition[1] = player.y + eyeHeight + offset[1];
eyePosition[2] = player.z + offset[2];

calculateDirection(eyeLookDir, lookDir[0], lookDir[1]);

if (getTargetBlock(eyePosition, eyeLookDir, maxDistance, interactBlockVec, expectedBlockFace)) {
return true; // If any possible face matches the client-side placement, assume it's legitimate
}
}
}
}

return false; // No matching face found
}

// Helper method to calculate direction (replace the Ray class method)
private void calculateDirection(double[] result, double xRot, double yRot) {
float rotX = (float) Math.toRadians(xRot);
float rotY = (float) Math.toRadians(yRot);
result[1] = -player.trigHandler.sin(rotY);
double xz = player.trigHandler.cos(rotY);
result[0] = -xz * player.trigHandler.sin(rotX);
result[2] = xz * player.trigHandler.cos(rotX);
}

private boolean getTargetBlock(double[] eyePosition, double[] eyeDirection, double maxDistance, Vector3i targetBlockVec, BlockFace expectedBlockFace) {
HitData hitData = CheckManagerListener.getNearestReachHitResult(player, eyePosition, eyeDirection, maxDistance, maxDistance, targetBlockVec, expectedBlockFace);
return hitData != null // Player is inside the block, from expanded hitbox, do we still need this?
&& targetBlockVec.equals(hitData.getPosition());
}

private boolean isBlockTypeWhitelisted(StateType type) {
return blockWhitelist.contains(type);
}

@Override
public void reload() {
super.reload();

useBlockWhitelist = getConfig().getBooleanElse("LineOfSightPlace.use-block-whitelist", false);
blockWhitelist = new HashSet<>();
List<String> blocks = getConfig().getList("LineOfSightPlace.block-whitelist");
Axionize marked this conversation as resolved.
Show resolved Hide resolved
for (String block : blocks) {
blockWhitelist.add(StateTypes.getByName(block));
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.protocol.world.Location;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
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.protocol.world.states.type.StateValue;
Expand Down Expand Up @@ -778,6 +779,62 @@ private static void placeLilypad(GrimPlayer player, InteractionHand hand) {
}
}

public static HitData getNearestReachHitResult(GrimPlayer player, double[] eyePos, double[] lookVec, double currentDistance, double maxDistance, Vector3i targetBlockVec, BlockFace expectedBlockFace) {
Vector3d startingPos = new Vector3d(eyePos[0], eyePos[1], eyePos[2]);
Vector startingVec = new Vector(startingPos.getX(), startingPos.getY(), startingPos.getZ());

// Convert double[] to Vector for Ray constructor
Vector eyePosVector = new Vector(eyePos[0], eyePos[1], eyePos[2]);
Vector lookVecVector = new Vector(lookVec[0], lookVec[1], lookVec[2]);
Ray trace = new Ray(eyePosVector, lookVecVector);
Vector endVec = trace.getPointAtDistance(maxDistance);
Vector3d endPos = new Vector3d(endVec.getX(), endVec.getY(), endVec.getZ());

StateType heldItem = null;
boolean checkInside = true;

return traverseBlocks(player, startingPos, endPos, (block, vector3i) -> {
ClientVersion clientVersion = player.getClientVersion();
CollisionBox data = HitboxData.getBlockHitbox(player, heldItem, clientVersion, block, vector3i.getX(), vector3i.getY(), vector3i.getZ());
List<SimpleCollisionBox> boxes = new ArrayList<>();
data.downCast(boxes);

double bestHitResult = Double.MAX_VALUE;
Vector bestHitLoc = null;
BlockFace bestFace = null;

// BEWARE OF https://bugs.mojang.com/browse/MC-85109 FOR 1.8 PLAYERS
// 1.8 Brewing Stand hitbox is a fullblock until it is hit sometimes, can be caused be restarting client and joining server
if (vector3i.equals(targetBlockVec) && clientVersion.isNewerThan(ClientVersion.V_1_7_10) && clientVersion.isOlderThan(ClientVersion.V_1_9) && block.getType() == StateTypes.BREWING_STAND) {
Axionize marked this conversation as resolved.
Show resolved Hide resolved
boxes.add(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true));
}

for (SimpleCollisionBox box : boxes) {
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(currentDistance));
if (intercept.getFirst() == null || intercept.getSecond() != expectedBlockFace) continue; // No intercept or wrong blockFace

Vector hitLoc = intercept.getFirst();

// If inside a block, return empty result for reach check
if (checkInside && ReachUtils.isVecInside(box, trace.getOrigin())) {
return null;
}

if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
bestHitResult = hitLoc.distanceSquared(startingVec);
bestHitLoc = hitLoc;
bestFace = intercept.getSecond();
}
}

if (bestHitLoc != null) {
return new HitData(vector3i, bestHitLoc, bestFace, block);
}

return null;
});
}

private static HitData getNearestHitResult(GrimPlayer player, StateType heldItem, boolean sourcesHaveHitbox) {
Vector3d startingPos = new Vector3d(player.x, player.y + player.getEyeHeight(), player.z);
Vector startingVec = new Vector(startingPos.getX(), startingPos.getY(), startingPos.getZ());
Expand Down Expand Up @@ -836,4 +893,8 @@ public void onPacketSend(PacketSendEvent event) {

player.checkManager.onPacketSend(event);
}

private static boolean isFence(StateType state) {
return BlockTags.FENCES.contains(state);
Axionize marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading