Compare commits

...
22 Commits
Author SHA1 Message Date
Axionize e4afe412a5 Revert "Remove unncessary NO_COLLISION entry for torchcrop"
This reverts commit e149ca6be8.
2024-11-03 19:30:08 -05:00
Axionize 45f5b5060c Revert "Remove unncessary NO_COLLISION entries"
This reverts commit 1761d853de.
2024-11-03 19:30:08 -05:00
Axionize a6af4cbbd9 Fix scaffolding hitbox for pre-1.14 clients 2024-11-03 07:06:50 -05:00
Axionize e76a59f14a Enable checks against scaffolding 2024-11-03 07:06:50 -05:00
Axionize ea2673541c Fix Weeping Vines and Vine Plant for all versions; Fix Weeping + Twisted for pre 1.16 players 2024-11-03 06:39:15 -05:00
Axionize 0193d342e0 Fix pitcher crop for pre-1.20 players 2024-11-03 06:39:13 -05:00
Axionize 0a1881592a Fix sniffer egg for pre 1.20 players 2024-11-03 06:39:13 -05:00
Axionize fcfb5d0cbb Fix frogspawn for pre-1.19 players 2024-11-03 06:39:12 -05:00
Axionize a8fc80dd50 Fix Sculk Shrieker hit box for all versions and collision box for pre 1.18.2 2024-11-03 06:39:12 -05:00
Axionize 8805899f39 Fix Spore Blossom hitbox on < 1.17 2024-11-03 06:39:12 -05:00
Axionize 5dc6d47dd0 Fix glow lichen and sculk vein boxes 2024-11-03 06:39:12 -05:00
Axionize 4450bd5c5d Fix Cauldron Hit/Collision Box 2024-11-03 03:52:52 -05:00
Axionize 45faec8290 Remove unncessary NO_COLLISION entries 2024-11-03 02:58:45 -05:00
Axionize a19a70813c Fix Pink Petals Boxes 2024-11-03 02:52:03 -05:00
Axionize 34582ffe50 Add new union() function to collision boxes 2024-11-03 02:52:03 -05:00
Axionize 0d4c1ed81e Fix Coral Fan Collision box 2024-11-03 02:52:03 -05:00
Axionize e1d739be1b Remove unncessary NO_COLLISION entry for torchcrop 2024-11-03 01:18:02 -04:00
Axionize 2c72efd3ea Cleanup and documnet why 2nd rayTrace in getNearestReachHitResult is intentionally non-optimal 2024-11-03 00:49:39 -04:00
Axionize 270f160dd2 Remove unsued getFacing() from vector code 2024-11-02 22:37:58 -04:00
Axionize b026f494d8 Fix falses on blocks with 'insides/internal walls' (different Raycast block placement shape then HitBox shape) like Cauldrons, Hoppers, and Composters 2024-11-02 22:37:57 -04:00
Axionize 8a690e2423 Annotate LOSP functions 2024-11-02 22:37:57 -04:00
Axionize 08678e4252 Fix falses on dead corals 2024-11-01 21:25:40 -04:00
12 changed files with 395 additions and 92 deletions

No files matched your search

@@ -5,8 +5,10 @@ import ac.grim.grimac.checks.CheckData;
import ac.grim.grimac.checks.type.BlockPlaceCheck;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.RaycastData;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import ac.grim.grimac.utils.data.HitData;
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
@@ -14,8 +16,11 @@ 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.Vector3i;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.HashSet;
import java.util.List;
@CheckData(name = "LineOfSightPlace")
public class LineOfSightPlace extends BlockPlaceCheck {
@@ -66,7 +71,6 @@ public class LineOfSightPlace extends BlockPlaceCheck {
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
@@ -168,10 +172,12 @@ public class LineOfSightPlace extends BlockPlaceCheck {
result[2] = xz * player.trigHandler.cos(rotX);
}
private boolean getTargetBlock(double[] eyePosition, double[] eyeDirection, double maxDistance, int[] targetBlockVec, BlockFace expectedBlockFace) {
Pair<int[], BlockFace> hitData = BlockRayTrace.getNearestReachHitResult(player, eyePosition, eyeDirection, maxDistance, maxDistance, targetBlockVec, expectedBlockFace);
private boolean getTargetBlock(double[] eyePos, double[] eyeDir, double maxDistance, int[] targetBlockVec, BlockFace expectedBlockFace) {
HitData hitData = BlockRayTrace.getNearestReachHitResult(player, eyePos, eyeDir, maxDistance, maxDistance, targetBlockVec, false);
// we check for hitdata != null because of being in expanded hitbox, or there was no result, do we still need this?
return hitData != null && Arrays.equals(targetBlockVec, hitData.getFirst()) && hitData.getSecond() == expectedBlockFace;
return hitData != null && new Vector3i(targetBlockVec[0], targetBlockVec[1], targetBlockVec[2]).equals(hitData.getPosition()) && hitData.getClosestDirection() == expectedBlockFace;
}
private boolean isBlockTypeWhitelisted(StateType type) {
@@ -24,6 +24,7 @@ import com.viaversion.viaversion.api.Via;
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
import java.util.*;
import java.util.stream.Stream;
// Warning for major game updates!
// Do not use an enum for stuff like Axis and other data types not in 1.7
@@ -193,8 +194,6 @@ public enum CollisionData {
}
}, StateTypes.PIGLIN_WALL_HEAD),
CORAL_FAN(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 4.0D, 14.0D), BlockTags.CORALS.getStates().toArray(new StateType[0])),
DOOR(new DoorHandler(), BlockTags.DOORS.getStates().toArray(new StateType[0])),
HOPPER((player, version, data, x, y, z) -> {
@@ -259,6 +258,8 @@ public enum CollisionData {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D);
}, StateTypes.STONECUTTER),
CORAL_FAN(NoCollisionBox.INSTANCE, BlockTags.CORALS.getStates().toArray(new StateType[0])),
RAILS(NoCollisionBox.INSTANCE, BlockTags.RAILS.getStates().toArray(new StateType[0])),
BANNER(NoCollisionBox.INSTANCE, BlockTags.BANNERS.getStates().toArray(new StateType[0])),
@@ -409,33 +410,6 @@ public enum CollisionData {
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 15.0D, 15.0D); // post
}, StateTypes.HONEY_BLOCK),
SPORE_BLOSSOM(new HexCollisionBox(2.0D, 13.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SPORE_BLOSSOM),
GLOW_LICHEN((player, version, data, x, y, z) -> {
ComplexCollisionBox box = new ComplexCollisionBox();
if (data.isUp()) {
box.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
}
if (data.isDown()) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D));
}
if (data.getWest() == West.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D));
}
if (data.getEast() == East.TRUE) {
box.add(new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D));
}
if (data.getNorth() == North.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D));
}
if (data.getSouth() == South.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D));
}
return box;
}, StateTypes.GLOW_LICHEN),
DRAGON_EGG_BLOCK(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.DRAGON_EGG),
GRINDSTONE((player, version, data, x, y, z) -> {
@@ -708,17 +682,36 @@ public enum CollisionData {
}, StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
CAULDRON((player, version, data, x, y, z) -> {
double height = 0.25;
if (version.isNewerThan(ClientVersion.getById(467))) { // changed in 19w13a, 1.14 Snapshot
return new ComplexCollisionBox(
new SimpleCollisionBox(0.0, 0.0, 0.0, 0.125, 1.0, 0.25, false),
new SimpleCollisionBox(0.0, 0.0, 0.75, 0.125, 1.0, 1.0, false),
new SimpleCollisionBox(0.125, 0.0, 0.0, 0.25, 1.0, 0.125, false),
new SimpleCollisionBox(0.125, 0.0, 0.875, 0.25, 1.0, 1.0, false),
new SimpleCollisionBox(0.75, 0.0, 0.0, 1.0, 1.0, 0.125, false),
new SimpleCollisionBox(0.75, 0.0, 0.875, 1.0, 1.0, 1.0, false),
new SimpleCollisionBox(0.875, 0.0, 0.125, 1.0, 1.0, 0.25, false),
new SimpleCollisionBox(0.875, 0.0, 0.75, 1.0, 1.0, 0.875, false),
new SimpleCollisionBox(0.0, 0.1875, 0.25, 1.0, 0.25, 0.75, false),
new SimpleCollisionBox(0.125, 0.1875, 0.125, 0.875, 0.25, 0.25, false),
new SimpleCollisionBox(0.125, 0.1875, 0.75, 0.875, 0.25, 0.875, false),
new SimpleCollisionBox(0.25, 0.1875, 0.0, 0.75, 1.0, 0.125, false),
new SimpleCollisionBox(0.25, 0.1875, 0.875, 0.75, 1.0, 1.0, false),
new SimpleCollisionBox(0.0, 0.25, 0.25, 0.125, 1.0, 0.75, false),
new SimpleCollisionBox(0.875, 0.25, 0.25, 1.0, 1.0, 0.75, false)
);
} else {
double height = 0.25;
if (version.isOlderThan(ClientVersion.V_1_13))
height = 0.3125;
if (version.isOlderThan(ClientVersion.V_1_13))
height = 0.3125;
return new ComplexCollisionBox(
new SimpleCollisionBox(0, 0, 0, 1, height, 1, false),
new SimpleCollisionBox(0, height, 0, 0.125, 1, 1, false),
new SimpleCollisionBox(1 - 0.125, height, 0, 1, 1, 1, false),
new SimpleCollisionBox(0, height, 0, 1, 1, 0.125, false),
new SimpleCollisionBox(0, height, 1 - 0.125, 1, 1, 1, false));
return new ComplexCollisionBox(
new SimpleCollisionBox(0, 0, 0, 1, height, 1, false),
new SimpleCollisionBox(0, height, 0, 0.125, 1, 1, false),
new SimpleCollisionBox(1 - 0.125, height, 0, 1, 1, 1, false),
new SimpleCollisionBox(0, height, 0, 1, 1, 0.125, false),
new SimpleCollisionBox(0, height, 1 - 0.125, 1, 1, 1, false));
}
}, BlockTags.CAULDRONS.getStates().toArray(new StateType[0])),
CACTUS(new SimpleCollisionBox(0.0625, 0, 0.0625,
@@ -791,7 +784,14 @@ public enum CollisionData {
CORAL_PLANT((player, version, data, x, y, z) -> {
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D);
}, BlockTags.CORAL_PLANTS.getStates().toArray(new StateType[0])),
}, Stream.concat(
Arrays.stream(BlockTags.CORAL_PLANTS.getStates().toArray(new StateType[0])),
Stream.of(StateTypes.DEAD_HORN_CORAL, StateTypes.DEAD_TUBE_CORAL, StateTypes.DEAD_BRAIN_CORAL,
StateTypes.DEAD_BUBBLE_CORAL, StateTypes.DEAD_FIRE_CORAL)
)
.distinct() // This will remove duplicates
.toArray(StateType[]::new)
),
SIGN(new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 1.0, 0.75, false),
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
@@ -889,7 +889,13 @@ public enum CollisionData {
SCULK_SENSOR(new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 8.0, 16.0), StateTypes.SCULK_SENSOR, StateTypes.CALIBRATED_SCULK_SENSOR),
DECORATED_POT(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0), StateTypes.DECORATED_POT),
DECORATED_POT((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_19_3)) {
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0);
} else { // ViaVersion replacement is a Brick
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.DECORATED_POT),
BIG_DRIPLEAF((player, version, data, x, y, z) -> {
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
@@ -1001,20 +1007,34 @@ public enum CollisionData {
}
}, StateTypes.MANGROVE_PROPAGULE),
SCULK_SHRIKER(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), StateTypes.SCULK_SHRIEKER),
SCULK_SHRIKER((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_18_2)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
} else {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.SCULK_SHRIEKER),
FROGSPAWN(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.5D, 16.0D), StateTypes.FROGSPAWN),
SNIFFER_EGG(new HexCollisionBox(1.0D, 0.0D, 2.0D, 15.0D, 16.0D, 14.0D), StateTypes.SNIFFER_EGG),
SNIFFER_EGG((player, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
return new HexCollisionBox(1.0D, 0.0D, 2.0D, 15.0D, 16.0D, 14.0D);
} else {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.SNIFFER_EGG),
PITCHER_CROP((player, version, data, x, y, z) -> {
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
if (data.getAge() == 0) {
return COLLISION_SHAPE_BULB;
if (data.getAge() == 0) {
return COLLISION_SHAPE_BULB;
} else {
return data.getHalf() == Half.LOWER ? COLLISION_SHAPE_CROP : NoCollisionBox.INSTANCE;
}
} else {
return data.getHalf() == Half.LOWER ? COLLISION_SHAPE_CROP : NoCollisionBox.INSTANCE;
return NoCollisionBox.INSTANCE;
}
}, StateTypes.PITCHER_CROP),
@@ -357,15 +357,56 @@ public enum HitboxData {
return common;
}, StateTypes.LECTERN),
PITCHER_CROP((player, item, version, data, x, y, z) -> {
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
GLOW_LICHEN_SCULK_VEIN((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
ComplexCollisionBox box = new ComplexCollisionBox();
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
if (data.isUp()) {
box.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
}
if (data.isDown()) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D));
}
if (data.getWest() == West.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D));
}
if (data.getEast() == East.TRUE) {
box.add(new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D));
}
if (data.getNorth() == North.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D));
}
if (data.getSouth() == South.TRUE) {
box.add(new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D));
}
return box;
} else { // ViaVersion just replaces this with... nothing
return NoCollisionBox.INSTANCE;
}
}, StateTypes.GLOW_LICHEN, StateTypes.SCULK_CATALYST),
SPORE_BLOSSOM((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
return new HexCollisionBox(2.0D, 13.0D, 2.0D, 14.0D, 16.0D, 14.0D);
} else { // ViaVersion replacement is a Peony
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.SPORE_BLOSSOM),
PITCHER_CROP((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
} else {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
}, StateTypes.PITCHER_CROP),
WHEAT_BEETROOTS((player, item, version, data, x, y, z) -> {
@@ -420,7 +461,7 @@ public enum HitboxData {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}, StateTypes.SWEET_BERRY_BUSH),
PINK_PETALS_BLOCK(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D), StateTypes.PINK_PETALS),
CORAL_FAN(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 4.0D, 14.0D), BlockTags.CORALS.getStates().toArray(new StateType[0])),
TORCHFLOWER_CROP((player, item, version, data, x, y, z) -> {
if (data.getAge() == 0) {
@@ -455,9 +496,18 @@ public enum HitboxData {
CAVE_VINES(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT),
TWISTING_VINES_BLOCK(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 15.0D, 12.0D), StateTypes.TWISTING_VINES, StateTypes.WEEPING_VINES),
// Then your enum entries become:
TWISTING_VINES_BLOCK((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, false, true), StateTypes.TWISTING_VINES),
TWISTING_VINES(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D), StateTypes.TWISTING_VINES_PLANT, StateTypes.WEEPING_VINES_PLANT),
WEEPING_VINES_BLOCK((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, true, true), StateTypes.WEEPING_VINES),
TWISTING_VINES((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, false, false), StateTypes.TWISTING_VINES_PLANT),
WEEPING_VINES((player, item, version, data, x, y, z) ->
getVineCollisionBox(version, true, false), StateTypes.WEEPING_VINES_PLANT),
TALL_PLANT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.TALL_GRASS, StateTypes.LARGE_FERN),
@@ -470,8 +520,8 @@ public enum HitboxData {
}, StateTypes.BAMBOO_SAPLING),
SCAFFOLDING((player, item, version, data, x, y, z) -> {
// If is holding scaffolding
if (item == StateTypes.SCAFFOLDING) {
// If is holding scaffolding or Via replacement - hay bale
if (item == StateTypes.SCAFFOLDING || version.isOlderThan(ClientVersion.V_1_14)) {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}
@@ -519,14 +569,54 @@ public enum HitboxData {
}, StateTypes.BIG_DRIPLEAF),
PINK_PETALS_BLOCK((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_20_2)) {
int flowerAmount = data.getFlowerAmount();
int horizontalIndex = getHorizontalID(data.getFacing());
CollisionBox result = NoCollisionBox.INSTANCE;
// Pre-defined collision boxes for each quadrant
HexCollisionBox[] boxes = new HexCollisionBox[] {
new HexCollisionBox(8, 0, 8, 16, 3, 16), // SE
new HexCollisionBox(8, 0, 0, 16, 3, 8), // NE
new HexCollisionBox(0, 0, 0, 8, 3, 8), // NW
new HexCollisionBox(0, 0, 8, 8, 3, 16) // SW
};
// Add boxes based on flower amount and facing
for (int i = 0; i < flowerAmount; i++) {
int index = Math.floorMod(i - horizontalIndex, 4);
result = result.union(boxes[index]);
}
return result;
} else if (version.isNewerThan(ClientVersion.V_1_19_3)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D);
} else if (version.isNewerThan(ClientVersion.V_1_12_2)) {
return CORAL_FAN.dynamic.fetch(player, item, version, data, x, y, z);
}
return GRASS_FERN.dynamic.fetch(player, item, version, data, x, y, z);
}, StateTypes.PINK_PETALS),
MANGROVE_PROPAGULE(((player, item, version, data, x, y, z) -> {
if (data.isHanging()) {
return new HexOffsetCollisionBox(data.getType(), 7.0, 0.0, 7.0, 9.0, 16.0, 9.0);
} else {
return new HexOffsetCollisionBox(data.getType(), 7.0, getPropaguleMinHeight(data.getAge()), 7.0, 9.0, 16.0, 9.0);
}
}), StateTypes.MANGROVE_PROPAGULE);
}), StateTypes.MANGROVE_PROPAGULE),
FROGSPAWN((player, item, version, data, x, y, z) -> {
if (version.isNewerThan(ClientVersion.V_1_18_2)) {
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.5D, 16.0D);
} else { // ViaVersion just replaces this with... nothing
return NoCollisionBox.INSTANCE;
}
}, StateTypes.FROGSPAWN),
// always a fullblock hitbox. Via replacement is obsidian
SCULK_SHRIKER(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SCULK_SHRIEKER);
private static final Map<StateType, HitboxData> lookup = new HashMap<>();
@@ -591,4 +681,41 @@ public enum HitboxData {
}
throw new IllegalStateException("Impossible Propagule Height");
}
private static CollisionBox getVineCollisionBox(ClientVersion version, boolean isWeeping, boolean isBlock) {
if (version.isNewerThan(ClientVersion.V_1_15_2)) {
if (isWeeping) {
return isBlock
? new HexCollisionBox(4.0, 9.0, 4.0, 12.0, 16.0, 12.0)
: new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 16.0, 15.0);
} else {
return new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, isBlock ? 15.0D : 16.0D, 12.0D);
}
} else {
// Via replacement - 4 sided vine
return new ComplexCollisionBox(
new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D),
new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D),
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D),
new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D)
);
}
}
private static int getHorizontalID(BlockFace facing) {
switch (facing) {
case DOWN:
case UP:
return -1;
case NORTH:
return 2;
case SOUTH:
return 0;
case WEST:
return 1;
case EAST:
return 3;
}
throw new IllegalStateException("Impossible blockface for getHorizontalID");
}
}
@@ -0,0 +1,98 @@
package ac.grim.grimac.utils.collisions;
/*
*/
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.*;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
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 org.jetbrains.annotations.Nullable;
import java.util.*;
// Expansion to the CollisionData class, which is different than regular ray tracing hitboxes
public enum RaycastData {
HOPPER((player, item, version, data, x, y, z) -> {
CollisionBox insideShape = new HexCollisionBox(2.0, 11.0, 2.0, 14.0, 16.0, 14.0);
switch (data.getFacing()) {
case NORTH:
new ComplexCollisionBox(insideShape, new HexCollisionBox(6.0, 8.0, 0.0, 10.0, 10.0, 4.0));
case SOUTH:
new ComplexCollisionBox(insideShape, new HexCollisionBox(6.0, 8.0, 12.0, 10.0, 10.0, 16.0));
case WEST:
new ComplexCollisionBox(insideShape, new HexCollisionBox(0.0, 8.0, 6.0, 4.0, 10.0, 10.0));
case EAST:
new ComplexCollisionBox(insideShape, new HexCollisionBox(12.0, 8.0, 6.0, 16.0, 10.0, 10.0));
default:
return insideShape;
}
}, StateTypes.HOPPER),
CAULDRON((player, item, version, data, x, y, z) -> {
return new HexCollisionBox(2.0, 4.0, 2.0, 14.0, 16.0, 14.0);
}, BlockTags.CAULDRONS.getStates().toArray(new StateType[0])),
FULL_BLOCK((player, item, version, data, x, y, z) -> {
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
}, StateTypes.COMPOSTER, StateTypes.SCAFFOLDING);
private static final Map<StateType, RaycastData> lookup = new HashMap<>();
static {
for (RaycastData data : RaycastData.values()) {
for (StateType type : data.materials) {
lookup.put(type, data);
}
}
}
private final StateType[] materials;
private CollisionBox box;
private HitBoxFactory dynamic;
RaycastData(CollisionBox box, StateType... materials) {
this.box = box;
Set<StateType> mList = new HashSet<>(Arrays.asList(materials));
mList.remove(null); // Sets can contain one null
this.materials = mList.toArray(new StateType[0]);
}
RaycastData(HitBoxFactory dynamic, StateType... materials) {
this.dynamic = dynamic;
Set<StateType> mList = new HashSet<>(Arrays.asList(materials));
mList.remove(null); // Sets can contain one null
this.materials = mList.toArray(new StateType[0]);
}
public static RaycastData getData(StateType material) {
return lookup.get(material);
}
@Nullable
public static CollisionBox getBlockHitbox(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
RaycastData data = getData(block.getType());
if (data == null) {
// We explicitly do not want to fallback on HitBoxData or CollisionBox
// Because we only ever use this as a 2nd ray trace
return NoCollisionBox.INSTANCE;
}
// Simple collision box to override
if (data.box != null)
return data.box.copy().offset(x, y, z);
// Allow this class to override collision boxes when they aren't the same as regular boxes
HitBoxFactory hitBoxFactory = data.dynamic;
CollisionBox collisionBox = hitBoxFactory.fetch(player, heldItem, version, block, x, y, z);
collisionBox.offset(x, y, z);
return collisionBox;
}
}
@@ -5,6 +5,8 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace;
import java.util.List;
public interface CollisionBox {
CollisionBox union(SimpleCollisionBox other);
boolean isCollided(SimpleCollisionBox other);
boolean isIntersected(SimpleCollisionBox other);
@@ -16,6 +16,12 @@ public class ComplexCollisionBox implements CollisionBox {
return boxes.add(collisionBox);
}
@Override
public CollisionBox union(SimpleCollisionBox other) {
add(other);
return this;
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
for (CollisionBox box : boxes) {
@@ -21,6 +21,14 @@ public class DynamicCollisionBox implements CollisionBox {
this.block = block;
}
// Untested but currently unused
// *should* work because every single one of these eventually becomes a Complex, Simple, or NoCollision Box
@Override
public CollisionBox union(SimpleCollisionBox other) {
CollisionBox dynamicBox = box.fetch(player, version, block, x, y, z).offset(x, y, z);
return dynamicBox.union(other);
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
return box.fetch(player, version, block, x, y, z).offset(x, y, z).isCollided(other);
@@ -9,6 +9,11 @@ public class NoCollisionBox implements CollisionBox {
private NoCollisionBox() {
}
@Override
public CollisionBox union(SimpleCollisionBox other) {
return other;
}
@Override
public boolean isCollided(SimpleCollisionBox other) {
return false;
@@ -163,7 +163,8 @@ public class SimpleCollisionBox implements CollisionBox {
return vectors;
}
public SimpleCollisionBox union(SimpleCollisionBox other) {
@Override
public CollisionBox union(SimpleCollisionBox other) {
this.minX = Math.min(this.minX, other.minX);
this.minY = Math.min(this.minY, other.minY);
this.minZ = Math.min(this.minZ, other.minZ);
@@ -1,5 +1,7 @@
package ac.grim.grimac.utils.math;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import com.github.retrooper.packetevents.util.Vector3d;
import lombok.experimental.UtilityClass;
import java.util.List;
@@ -2,6 +2,7 @@ package ac.grim.grimac.utils.nmsutil;
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.HitboxData;
import ac.grim.grimac.utils.collisions.RaycastData;
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.NoCollisionBox;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
@@ -17,6 +18,8 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
@@ -29,6 +32,7 @@ public class BlockRayTrace {
//
// I do have to admit that I'm starting to like bifunctions/new java 8 things more than I originally did.
// although I still don't understand Mojang's obsession with streams in some of the hottest methods... that kills performance
@Nullable
public static Pair<int[], BlockFace> traverseBlocksLOSP(GrimPlayer player, double[] start, double[] end, BiFunction<WrappedBlockState, int[], Pair<int[], BlockFace>> predicate) {
// I guess go back by the collision epsilon?
double endX = GrimMath.lerp(-1.0E-7D, end[0], start[0]);
@@ -168,22 +172,21 @@ public class BlockRayTrace {
return traverseBlocks(player, new double[]{start.x, start.y, start.z}, new double[]{end.x, end.y, end.z}, predicate);
}
public static Pair<int[], BlockFace> getNearestReachHitResult(GrimPlayer player, double[] eyePos, double[] lookVec, double currentDistance, double maxDistance, int[] targetBlockVec, BlockFace expectedBlockFace) {
@Nullable
public static HitData getNearestReachHitResult(GrimPlayer player, double[] startPos, double[] lookVec, double currentDistance, double maxDistance, int[] targetBlockVec, boolean raycastContext) {
double[] endPos = new double[]{
eyePos[0] + lookVec[0] * maxDistance,
eyePos[1] + lookVec[1] * maxDistance,
eyePos[2] + lookVec[2] * maxDistance
startPos[0] + lookVec[0] * maxDistance,
startPos[1] + lookVec[1] * maxDistance,
startPos[2] + lookVec[2] * maxDistance
};
double[] currentEnd = new double[]{
eyePos[0] + lookVec[0] * currentDistance,
eyePos[1] + lookVec[1] * currentDistance,
eyePos[2] + lookVec[2] * currentDistance
};
return traverseBlocksLOSP(player, eyePos, endPos, (block, vector3i) -> {
ClientVersion clientVersion = player.getClientVersion();
CollisionBox data = HitboxData.getBlockHitbox(player, null, clientVersion, block, vector3i[0], vector3i[1], vector3i[2]);
return traverseBlocks(player, startPos, endPos, (block, vector3i) -> {
CollisionBox data;
if (!raycastContext) {
data = HitboxData.getBlockHitbox(player, player.getInventory().getHeldItem().getType().getPlacedType(), player.getClientVersion(), block, vector3i.x, vector3i.y, vector3i.z);
} else {
data = RaycastData.getBlockHitbox(player, null, player.getClientVersion(), block, vector3i.x, vector3i.y, vector3i.z);
}
if (data == NoCollisionBox.INSTANCE) return null;
List<SimpleCollisionBox> boxes = new ArrayList<>();
data.downCast(boxes);
@@ -194,21 +197,23 @@ public class BlockRayTrace {
// 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 (block.getType() == StateTypes.BREWING_STAND && clientVersion.equals(ClientVersion.V_1_8) && Arrays.equals(vector3i, targetBlockVec)) {
if (block.getType() == StateTypes.BREWING_STAND && player.getClientVersion().equals(ClientVersion.V_1_8) && Arrays.equals(new int[]{vector3i.x, vector3i.y, vector3i.z}, targetBlockVec)) {
boxes.add(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true));
}
currentEnd[0] = eyePos[0] + lookVec[0] * currentDistance;
currentEnd[1] = eyePos[1] + lookVec[1] * currentDistance;
currentEnd[2] = eyePos[2] + lookVec[2] * currentDistance;
double[] currentEnd = new double[]{
startPos[0] + lookVec[0] * currentDistance,
startPos[1] + lookVec[1] * currentDistance,
startPos[2] + lookVec[2] * currentDistance
};
for (SimpleCollisionBox box : boxes) {
Pair<double[], BlockFace> intercept = ReachUtilsPrimitives.calculateIntercept(box, eyePos, currentEnd);
Pair<double[], BlockFace> intercept = ReachUtilsPrimitives.calculateIntercept(box, startPos, currentEnd);
if (intercept.getFirst() == null) continue; // No intercept or wrong blockFace
double[] hitLoc = intercept.getFirst();
double distSq = distanceSquared(hitLoc, eyePos);
double distSq = distanceSquared(hitLoc, startPos);
if (distSq < bestHitResult) {
bestHitResult = distSq;
bestHitLoc = hitLoc;
@@ -216,8 +221,29 @@ public class BlockRayTrace {
}
}
// Yes, this is not the most optimal algorithm for handling Cauldrons, Hoppers, Composters, and Scaffolding
// that is to say, blocks that have a different outline/hitbox shape from the box used to calculate placement blockfaces
// but it is the one vanilla uses
// No we will not
// 1. Calculate the ray trace from a new closer startPos to reduce iterations
// because it adds a lot of code complexity for very little performance gain
// 2. Run a switch case on the target block and check if the index of the SimpleCollisionBox corresponds to a wall with an inside face
// and hardcode in blockface fixes for placements against those compnents above a certain relative y level
// because that is version-specific, will break if the implementation of the returned ComplexCollisionBox changes
// and again, lots of code complexity for little performance gain
if (bestHitLoc != null) {
return new Pair<>(vector3i, bestFace);
HitData hitData = new HitData(vector3i, new Vector(bestHitLoc[0], bestHitLoc[1], bestHitLoc[2]), bestFace, block);
if (!raycastContext) {
HitData hitData2 = BlockRayTrace.getNearestReachHitResult(player, startPos, lookVec, maxDistance, maxDistance, targetBlockVec, true);
if (hitData2 != null) {
Vector startVector = new Vector(startPos[0], startPos[1], startPos[2]);
if (hitData2.getBlockHitLocation().subtract(startVector).lengthSquared() <
hitData.getBlockHitLocation().subtract(startVector).lengthSquared()) {
return new HitData(vector3i, hitData.getBlockHitLocation(), hitData2.getClosestDirection(), block);
}
}
}
return hitData;
}
return null;
@@ -5,9 +5,11 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.Pair;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
public class ReachUtilsPrimitives {
public static Pair<double[], BlockFace> calculateIntercept(SimpleCollisionBox self, double[] origin, double[] end) {
public static @NotNull Pair<@Nullable double[], @Nullable BlockFace> calculateIntercept(SimpleCollisionBox self, double[] origin, double[] end) {
double[] minX = getIntermediateWithXValue(origin, end, self.minX);
double[] maxX = getIntermediateWithXValue(origin, end, self.maxX);
double[] minY = getIntermediateWithYValue(origin, end, self.minY);