Compare commits
167
Commits
2.0
...
los-containers
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4afe412a5 | ||
|
|
45f5b5060c | ||
|
|
a6af4cbbd9 | ||
|
|
e76a59f14a | ||
|
|
ea2673541c | ||
|
|
0193d342e0 | ||
|
|
0a1881592a | ||
|
|
fcfb5d0cbb | ||
|
|
a8fc80dd50 | ||
|
|
8805899f39 | ||
|
|
5dc6d47dd0 | ||
|
|
4450bd5c5d | ||
|
|
45faec8290 | ||
|
|
a19a70813c | ||
|
|
34582ffe50 | ||
|
|
0d4c1ed81e | ||
|
|
e1d739be1b | ||
|
|
2c72efd3ea | ||
|
|
270f160dd2 | ||
|
|
b026f494d8 | ||
|
|
8a690e2423 | ||
|
|
08678e4252 | ||
|
|
980226fab9 | ||
|
|
0f726a0f34 | ||
|
|
0697c425ca | ||
|
|
428f213e15 | ||
|
|
593d48a6e6 | ||
|
|
3dcf3c931a | ||
|
|
8cc45ddba3 | ||
|
|
6f23122657 | ||
|
|
c0b0759ed0 | ||
|
|
00252f3b97 | ||
|
|
531c49011b | ||
|
|
dea22454f7 | ||
|
|
12bda63785 | ||
|
|
c65e17cf0c | ||
|
|
753ca0e6ad | ||
|
|
8b85e1711d | ||
|
|
12b0dfd19d | ||
|
|
8c05d41423 | ||
|
|
c5ab0f5440 | ||
|
|
d92f5ef375 | ||
|
|
c313d6203a | ||
|
|
070f92aea2 | ||
|
|
4e3c5894b5 | ||
|
|
8cfb9f0f17 | ||
|
|
4e135e3f92 | ||
|
|
938522974b | ||
|
|
6994975387 | ||
|
|
6d3a172466 | ||
|
|
3aaa185617 | ||
|
|
980cd92333 | ||
|
|
89da2942c7 | ||
|
|
8ea905e053 | ||
|
|
885e959cdb | ||
|
|
02904a3dce | ||
|
|
e8d4bb2376 | ||
|
|
7695167578 | ||
|
|
94e5f63a65 | ||
|
|
9fa0f773d5 | ||
|
|
88111e6f7e | ||
|
|
9a5c16fe6a | ||
|
|
4efaf8d497 | ||
|
|
07881da451 | ||
|
|
bd6c2524e7 | ||
|
|
b096e02b9b | ||
|
|
ad6b24345b | ||
|
|
9694b756b5 | ||
|
|
6b5786be9d | ||
|
|
1f49ab2551 | ||
|
|
b18a5bec8e | ||
|
|
d741459e2f | ||
|
|
7007722aca | ||
|
|
05e7b3c1e5 | ||
|
|
13554546eb | ||
|
|
e2f3722b52 | ||
|
|
1b75734046 | ||
|
|
61d9609328 | ||
|
|
4cff51c89f | ||
|
|
ace5178e99 | ||
|
|
4a9d8627d3 | ||
|
|
fc89b932dd | ||
|
|
0852ac8326 | ||
|
|
87194d7f1b | ||
|
|
31fa689a98 | ||
|
|
5d366e742c | ||
|
|
7cc3c8f5ef | ||
|
|
7215dfcf60 | ||
|
|
5c709c1c54 | ||
|
|
1e5ad65c9f | ||
|
|
f6aeffb264 | ||
|
|
4593f3077c | ||
|
|
b92c8726de | ||
|
|
c560539a16 | ||
|
|
9cba7a95f7 | ||
|
|
d6d86ef04d | ||
|
|
ed643f3481 | ||
|
|
d8c2f77a0b | ||
|
|
6cfda647f7 | ||
|
|
fc58fcc721 | ||
|
|
5f0707956a | ||
|
|
528712e931 | ||
|
|
8fe609452b | ||
|
|
3afd2c4ac9 | ||
|
|
5793d7b678 | ||
|
|
569a8b4c4d | ||
|
|
9cab8f7469 | ||
|
|
1ac0b2efbe | ||
|
|
b78a36f1c2 | ||
|
|
e23d191cb8 | ||
|
|
8854b00b46 | ||
|
|
4be835de07 | ||
|
|
b8f59fa99c | ||
|
|
2236efea4f | ||
|
|
344ba4138d | ||
|
|
ba0fa7a869 | ||
|
|
21668a63fd | ||
|
|
2f6f9bcbc5 | ||
|
|
86d19de452 | ||
|
|
d4986449f1 | ||
|
|
3fc366bf1a | ||
|
|
9d192b215e | ||
|
|
b4ea4517e9 | ||
|
|
07ad12a16f | ||
|
|
f7529a18df | ||
|
|
c66522cb47 | ||
|
|
94a31f77ff | ||
|
|
c0630f9f6d | ||
|
|
3a145cb615 | ||
|
|
837516ea86 | ||
|
|
66228f59be | ||
|
|
800c79a898 | ||
|
|
2177b12bcf | ||
|
|
050c22ce27 | ||
|
|
ded614f30f | ||
|
|
88e439ebfd | ||
|
|
a027355c7d | ||
|
|
8a351ec928 | ||
|
|
b2fa4974ef | ||
|
|
9c2a034b43 | ||
|
|
45ba746300 | ||
|
|
483f317f83 | ||
|
|
dee3268bce | ||
|
|
2a88d3eef4 | ||
|
|
2898fed15a | ||
|
|
d2721077c9 | ||
|
|
dd7bb81b46 | ||
|
|
4515d21023 | ||
|
|
3315e3c148 | ||
|
|
2b552680a7 | ||
|
|
f78c80f04d | ||
|
|
bc239a7994 | ||
|
|
a508ec2279 | ||
|
|
cccecbacbc | ||
|
|
177a8b0527 | ||
|
|
d869ff959f | ||
|
|
772ba562e6 | ||
|
|
1786360d7c | ||
|
|
328596f01f | ||
|
|
6a13c9f822 | ||
|
|
3c78a63719 | ||
|
|
8b645b75df | ||
|
|
ae3ea30c92 | ||
|
|
01bd2c0c11 | ||
|
|
e91721299a | ||
|
|
4e08ec9fca | ||
|
|
b599008040 |
No files matched your search
@@ -202,8 +202,9 @@ public class Reach extends Check implements PacketCheck {
|
||||
|
||||
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_ENTITY_INTERACTION_RANGE) + 3;
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
for (Vector lookVec : possibleLookDirs) {
|
||||
for (double eye : player.getPossibleEyeHeights()) {
|
||||
for (double eye : possibleEyeHeights) {
|
||||
Vector eyePos = new Vector(from.getX(), from.getY() + eye, from.getZ());
|
||||
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ public class FarPlace extends BlockPlaceCheck {
|
||||
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
|
||||
|
||||
double min = Double.MAX_VALUE;
|
||||
for (double d : player.getPossibleEyeHeights()) {
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
for (double d : possibleEyeHeights) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
|
||||
Vector eyes = new Vector(player.x, player.y + d, player.z);
|
||||
Vector best = VectorUtils.cutBoxToVector(eyes, box);
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
package ac.grim.grimac.checks.impl.scaffolding;
|
||||
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
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.HitData;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockRayTrace;
|
||||
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.Vector3i;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@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;
|
||||
|
||||
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 (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) {
|
||||
return !isBlockTypeWhitelisted(targetBlockStateType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean didRayTraceHit(BlockPlace place) {
|
||||
double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
|
||||
// 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);
|
||||
|
||||
int[] interactBlockVec = new int[]{place.blockPosition.x, place.blockPosition.y, place.blockPosition.z};
|
||||
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[0], interactBlockVec[1], interactBlockVec[2]))) {
|
||||
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.xRot, player.yRot},
|
||||
{player.lastXRot, player.lastYRot},
|
||||
{player.lastXRot, player.yRot}
|
||||
};
|
||||
} 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}};
|
||||
} else {
|
||||
possibleLookDirs = new double[][]{
|
||||
{player.xRot, player.yRot},
|
||||
{player.lastXRot, player.yRot}
|
||||
};
|
||||
}
|
||||
|
||||
// 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[] 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 && new Vector3i(targetBlockVec[0], targetBlockVec[1], targetBlockVec[2]).equals(hitData.getPosition()) && hitData.getClosestDirection() == expectedBlockFace;
|
||||
}
|
||||
|
||||
private boolean isBlockTypeWhitelisted(StateType type) {
|
||||
return blockWhitelist.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload(ConfigManager config) {
|
||||
useBlockWhitelist = config.getBooleanElse("LineOfSightPlace.use-block-whitelist", false);
|
||||
blockWhitelist = new HashSet<>();
|
||||
List<String> blocks = config.getStringList("LineOfSightPlace.block-whitelist");
|
||||
for (String block : blocks) {
|
||||
blockWhitelist.add(StateTypes.getByName(block));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,13 @@ public class PositionPlace extends BlockPlaceCheck {
|
||||
// Each position represents the best case scenario to have clicked
|
||||
//
|
||||
// We will now calculate the most optimal position for the player's head to be in
|
||||
double minEyeHeight = Collections.min(player.getPossibleEyeHeights());
|
||||
double maxEyeHeight = Collections.max(player.getPossibleEyeHeights());
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
double minEyeHeight = Double.MAX_VALUE;
|
||||
double maxEyeHeight = Double.MIN_VALUE;
|
||||
for (double height : possibleEyeHeights) {
|
||||
minEyeHeight = Math.min(minEyeHeight, height);
|
||||
maxEyeHeight = Math.max(maxEyeHeight, height);
|
||||
}
|
||||
// I love the idle packet, why did you remove it mojang :(
|
||||
// Don't give 0.03 lenience if the player is a 1.8 player and we know they couldn't have 0.03'd because idle packet
|
||||
double movementThreshold = !player.packetStateData.didLastMovementIncludePosition || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? player.getMovementThreshold() : 0;
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
package ac.grim.grimac.checks.impl.scaffolding;
|
||||
|
||||
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.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.nmsutil.Ray;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
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.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@CheckData(name = "RotationPlace")
|
||||
public class RotationPlace extends BlockPlaceCheck {
|
||||
double flagBuffer = 0; // If the player flags once, force them to play legit, or we will cancel the tick before.
|
||||
boolean ignorePost = false;
|
||||
|
||||
public RotationPlace(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(final BlockPlace place) {
|
||||
if (place.getMaterial() == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
|
||||
if (flagBuffer > 0 && !didRayTraceHit(place)) {
|
||||
ignorePost = true;
|
||||
// If the player hit and has flagged this check recently
|
||||
if (flagAndAlert("pre-flying") && 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 (place.getMaterial() == StateTypes.SCAFFOLDING) return;
|
||||
if (player.gamemode == GameMode.SPECTATOR) return; // you don't send flying packets when spectating entities
|
||||
|
||||
// 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");
|
||||
} else {
|
||||
flagBuffer = Math.max(0, flagBuffer - 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean didRayTraceHit(BlockPlace place) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(place.getPlacedAgainstBlockLocation());
|
||||
|
||||
List<Vector3f> possibleLookDirs = new ArrayList<>(Arrays.asList(
|
||||
new Vector3f(player.xRot, player.yRot, 0),
|
||||
new Vector3f(player.lastXRot, player.yRot, 0)
|
||||
));
|
||||
|
||||
// Start checking if player is in the block
|
||||
double minEyeHeight = Collections.min(player.getPossibleEyeHeights());
|
||||
double maxEyeHeight = Collections.max(player.getPossibleEyeHeights());
|
||||
|
||||
SimpleCollisionBox eyePositions = new SimpleCollisionBox(player.x, player.y + minEyeHeight, player.z, player.x, player.y + maxEyeHeight, player.z);
|
||||
eyePositions.expand(player.getMovementThreshold());
|
||||
|
||||
// If the player is inside a block, then they can ray trace through the block and hit the other side of the block
|
||||
if (eyePositions.isIntersected(box)) {
|
||||
return true;
|
||||
}
|
||||
// End checking if the player is in the block
|
||||
|
||||
// 1.9+ players could be a tick behind because we don't get skipped ticks
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
possibleLookDirs.add(new Vector3f(player.lastXRot, player.lastYRot, 0));
|
||||
}
|
||||
|
||||
// 1.7 players do not have any of these issues! They are always on the latest look vector
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
possibleLookDirs = Collections.singletonList(new Vector3f(player.xRot, player.yRot, 0));
|
||||
}
|
||||
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
|
||||
for (double d : player.getPossibleEyeHeights()) {
|
||||
for (Vector3f lookDir : possibleLookDirs) {
|
||||
// x, y, z are correct for the block placement even after post tick because of code elsewhere
|
||||
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
|
||||
// xRot and yRot are a tick behind
|
||||
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
|
||||
if (intercept.getFirst() != null) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,9 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.*;
|
||||
import ac.grim.grimac.utils.blockplace.BlockPlaceResult;
|
||||
import ac.grim.grimac.utils.blockplace.ConsumesBlockPlace;
|
||||
import ac.grim.grimac.utils.collisions.HitboxData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.*;
|
||||
import ac.grim.grimac.utils.inventory.Inventory;
|
||||
import ac.grim.grimac.utils.latency.CompensatedWorld;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import ac.grim.grimac.utils.nmsutil.*;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
@@ -24,7 +20,6 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.ConnectionState;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
@@ -36,6 +31,7 @@ import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
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;
|
||||
@@ -47,11 +43,6 @@ import com.github.retrooper.packetevents.wrapper.play.client.*;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
@@ -59,79 +50,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
super(PacketListenerPriority.LOW);
|
||||
}
|
||||
|
||||
// Copied from MCP...
|
||||
// Returns null if there isn't anything.
|
||||
//
|
||||
// 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
|
||||
public static HitData traverseBlocks(GrimPlayer player, Vector3d start, Vector3d end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
|
||||
// I guess go back by the collision epsilon?
|
||||
double endX = GrimMath.lerp(-1.0E-7D, end.x, start.x);
|
||||
double endY = GrimMath.lerp(-1.0E-7D, end.y, start.y);
|
||||
double endZ = GrimMath.lerp(-1.0E-7D, end.z, start.z);
|
||||
double startX = GrimMath.lerp(-1.0E-7D, start.x, end.x);
|
||||
double startY = GrimMath.lerp(-1.0E-7D, start.y, end.y);
|
||||
double startZ = GrimMath.lerp(-1.0E-7D, start.z, end.z);
|
||||
int floorStartX = GrimMath.floor(startX);
|
||||
int floorStartY = GrimMath.floor(startY);
|
||||
int floorStartZ = GrimMath.floor(startZ);
|
||||
|
||||
|
||||
if (start.equals(end)) return null;
|
||||
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
|
||||
HitData apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
|
||||
double xDiff = endX - startX;
|
||||
double yDiff = endY - startY;
|
||||
double zDiff = endZ - startZ;
|
||||
double xSign = Math.signum(xDiff);
|
||||
double ySign = Math.signum(yDiff);
|
||||
double zSign = Math.signum(zDiff);
|
||||
|
||||
double posXInverse = xSign == 0 ? Double.MAX_VALUE : xSign / xDiff;
|
||||
double posYInverse = ySign == 0 ? Double.MAX_VALUE : ySign / yDiff;
|
||||
double posZInverse = zSign == 0 ? Double.MAX_VALUE : zSign / zDiff;
|
||||
|
||||
double d12 = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
|
||||
double d13 = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
|
||||
double d14 = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
|
||||
|
||||
// Can't figure out what this code does currently
|
||||
while (d12 <= 1.0D || d13 <= 1.0D || d14 <= 1.0D) {
|
||||
if (d12 < d13) {
|
||||
if (d12 < d14) {
|
||||
floorStartX += xSign;
|
||||
d12 += posXInverse;
|
||||
} else {
|
||||
floorStartZ += zSign;
|
||||
d14 += posZInverse;
|
||||
}
|
||||
} else if (d13 < d14) {
|
||||
floorStartY += ySign;
|
||||
d13 += posYInverse;
|
||||
} else {
|
||||
floorStartZ += zSign;
|
||||
d14 += posZInverse;
|
||||
}
|
||||
|
||||
state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
|
||||
apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void placeWaterLavaSnowBucket(GrimPlayer player, ItemStack held, StateType toPlace, InteractionHand hand) {
|
||||
HitData data = getNearestHitResult(player, StateTypes.AIR, false);
|
||||
HitData data = BlockRayTrace.getNearestHitResult(player, StateTypes.AIR, false);
|
||||
if (data != null) {
|
||||
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), held, data);
|
||||
|
||||
@@ -268,7 +188,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
// The offhand is unable to interact with blocks like this... try to stop some desync points before they happen
|
||||
if ((!player.isSneaking || onlyAir) && place.getHand() == InteractionHand.MAIN_HAND) {
|
||||
Vector3i blockPosition = place.getBlockPosition();
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, getNearestHitResult(player, null, true));
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), place.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
|
||||
|
||||
// Right-clicking a trapdoor/door/etc.
|
||||
StateType placedAgainst = blockPlace.getPlacedAgainstMaterial();
|
||||
@@ -308,7 +228,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
placedWith = player.getInventory().getOffHand();
|
||||
}
|
||||
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, getNearestHitResult(player, null, true));
|
||||
BlockPlace blockPlace = new BlockPlace(player, place.getHand(), blockPosition, place.getFaceId(), face, placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
|
||||
// At this point, it is too late to cancel, so we can only flag, and cancel subsequent block places more aggressively
|
||||
if (!player.compensatedEntities.getSelf().inVehicle()) {
|
||||
player.checkManager.onPostFlyingBlockPlace(blockPlace);
|
||||
@@ -508,7 +428,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
player.placeUseItemPackets.add(new BlockPlaceSnapshot(packet, player.isSneaking));
|
||||
} else {
|
||||
// Anti-air place
|
||||
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, getNearestHitResult(player, null, true));
|
||||
BlockPlace blockPlace = new BlockPlace(player, packet.getHand(), packet.getBlockPosition(), packet.getFaceId(), packet.getFace(), placedWith, BlockRayTrace.getNearestHitResult(player, null, true));
|
||||
blockPlace.setCursor(packet.getCursorPosition());
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_11) && player.getClientVersion().isOlderThan(ClientVersion.V_1_11)) {
|
||||
@@ -582,7 +502,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
private static void placeBucket(GrimPlayer player, InteractionHand hand) {
|
||||
HitData data = getNearestHitResult(player, null, true);
|
||||
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
|
||||
|
||||
if (data != null) {
|
||||
BlockPlace blockPlace = new BlockPlace(player, hand, data.getPosition(), data.getClosestDirection().getFaceValue(), data.getClosestDirection(), ItemStack.EMPTY, data);
|
||||
@@ -748,7 +668,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
private static void placeLilypad(GrimPlayer player, InteractionHand hand) {
|
||||
HitData data = getNearestHitResult(player, null, true);
|
||||
HitData data = BlockRayTrace.getNearestHitResult(player, null, true);
|
||||
|
||||
if (data != null) {
|
||||
// A lilypad cannot replace a fluid
|
||||
@@ -778,56 +698,6 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
Ray trace = new Ray(player, startingPos.getX(), startingPos.getY(), startingPos.getZ(), player.xRot, player.yRot);
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
|
||||
Vector endVec = trace.getPointAtDistance(distance);
|
||||
Vector3d endPos = new Vector3d(endVec.getX(), endVec.getY(), endVec.getZ());
|
||||
|
||||
return traverseBlocks(player, startingPos, endPos, (block, vector3i) -> {
|
||||
CollisionBox data = HitboxData.getBlockHitbox(player, heldItem, player.getClientVersion(), 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;
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
if (intercept.getFirst() == null) continue; // No intercept
|
||||
|
||||
Vector hitLoc = intercept.getFirst();
|
||||
|
||||
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
|
||||
bestHitResult = hitLoc.distanceSquared(startingVec);
|
||||
bestHitLoc = hitLoc;
|
||||
bestFace = intercept.getSecond();
|
||||
}
|
||||
}
|
||||
if (bestHitLoc != null) {
|
||||
return new HitData(vector3i, bestHitLoc, bestFace, block);
|
||||
}
|
||||
|
||||
if (sourcesHaveHitbox &&
|
||||
(player.compensatedWorld.isWaterSourceBlock(vector3i.getX(), vector3i.getY(), vector3i.getZ())
|
||||
|| player.compensatedWorld.getLavaFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ()) == (8 / 9f))) {
|
||||
double waterHeight = player.compensatedWorld.getFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ());
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(vector3i.getX(), vector3i.getY(), vector3i.getZ(), vector3i.getX() + 1, vector3i.getY() + waterHeight, vector3i.getZ() + 1);
|
||||
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
|
||||
if (intercept.getFirst() != null) {
|
||||
return new HitData(vector3i, intercept.getFirst(), intercept.getSecond(), block);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getConnectionState() != ConnectionState.PLAY) return;
|
||||
@@ -836,4 +706,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
player.checkManager.onPacketSend(event);
|
||||
}
|
||||
|
||||
private static boolean isFence(StateType state) {
|
||||
return BlockTags.FENCES.contains(state);
|
||||
}
|
||||
}
|
||||
@@ -143,8 +143,8 @@ public class CheckManager {
|
||||
.put(FarPlace.class, new FarPlace(player))
|
||||
.put(FabricatedPlace.class, new FabricatedPlace(player))
|
||||
.put(PositionPlace.class, new PositionPlace(player))
|
||||
.put(RotationPlace.class, new RotationPlace(player))
|
||||
.put(DuplicateRotPlace.class, new DuplicateRotPlace(player))
|
||||
.put(LineOfSightPlace.class, new LineOfSightPlace(player))
|
||||
.put(GhostBlockMitigation.class, new GhostBlockMitigation(player))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ import net.kyori.adventure.text.TranslatableComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -200,6 +201,8 @@ public class GrimPlayer implements GrimUser {
|
||||
public long lastBlockPlaceUseItem = 0;
|
||||
public AtomicInteger cancelledPackets = new AtomicInteger(0);
|
||||
public MainSupportingBlockData mainSupportingBlockData = new MainSupportingBlockData(null, false);
|
||||
// possibleEyeHeights[0] = Standing eye heights, [1] = Sneaking. [2] = Elytra, Swimming, and Riptide Trident which only exists in 1.9+
|
||||
public double[][] possibleEyeHeights = new double[3][];
|
||||
|
||||
public void onPacketCancel() {
|
||||
if (spamThreshold != -1 && cancelledPackets.incrementAndGet() > spamThreshold) {
|
||||
@@ -241,6 +244,21 @@ public class GrimPlayer implements GrimUser {
|
||||
packetStateData = new PacketStateData();
|
||||
|
||||
uncertaintyHandler.collidingEntities.add(0);
|
||||
|
||||
if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) {
|
||||
final float scale = (float) compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_SCALE);
|
||||
possibleEyeHeights[2] = new double[]{0.4 * scale, 1.62 * scale, 1.27 * scale}; // Elytra, standing, sneaking (1.14)
|
||||
possibleEyeHeights[1] = new double[]{1.27 * scale, 1.62 * scale, 0.4 * scale}; // sneaking (1.14), standing, Elytra
|
||||
possibleEyeHeights[0] = new double[]{1.62 * scale, 1.27 * scale, 0.4 * scale}; // standing, sneaking (1.14), Elytra
|
||||
|
||||
} else if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) { // standing, sneaking Elytra
|
||||
possibleEyeHeights[2] = new double[]{0.4, 1.62, 1.54}; // Elytra, standing, sneaking (1.13)
|
||||
possibleEyeHeights[1] = new double[]{1.54, 1.62, 0.4}; // sneaking (1.9-1.13), standing, Elytra
|
||||
possibleEyeHeights[0] = new double[]{1.62, 1.54, 0.4}; // standing, sneaking (1.9-1.13), Elytra
|
||||
} else {
|
||||
possibleEyeHeights[1] = new double[]{(double) (1.62f - 0.08f), (double) (1.62f)}; // sneaking, standing
|
||||
possibleEyeHeights[0] = new double[]{(double) (1.62f), (double) (1.62f - 0.08f)}; // standing, sneaking
|
||||
}
|
||||
}
|
||||
|
||||
public Set<VectorData> getPossibleVelocities() {
|
||||
@@ -559,14 +577,23 @@ public class GrimPlayer implements GrimUser {
|
||||
return checkManager.getInventory();
|
||||
}
|
||||
|
||||
public List<Double> getPossibleEyeHeights() { // We don't return sleeping eye height
|
||||
if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)) { // Elytra, sneaking (1.14), standing
|
||||
final float scale = (float) compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_SCALE);
|
||||
return Arrays.asList(0.4 * scale, 1.27 * scale, 1.62 * scale);
|
||||
} else if (getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9)) { // Elytra, sneaking, standing
|
||||
return Arrays.asList(0.4, 1.54, 1.62);
|
||||
} else { // Only sneaking or standing
|
||||
return Arrays.asList((double) (1.62f - 0.08f), (double) (1.62f));
|
||||
public double[] getPossibleEyeHeights() { // We don't return sleeping eye height
|
||||
// 1.8 Players once again ruin my clean switch-case
|
||||
if (this.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
|
||||
return this.isSneaking ? this.possibleEyeHeights[1] : this.possibleEyeHeights[0];
|
||||
} else {
|
||||
// 1.8 players just have their pose set to standing all the time
|
||||
switch (pose) {
|
||||
case FALL_FLYING: // Elytra gliding
|
||||
case SPIN_ATTACK: // Riptide trident
|
||||
case SWIMMING: // Swimming (includes crawling in 1.14+)
|
||||
return this.possibleEyeHeights[2]; // [swimming/gliding/riptide height, standing height, sneaking height]
|
||||
case NINE_CROUCHING:
|
||||
case CROUCHING:
|
||||
return this.possibleEyeHeights[1]; // [sneaking height, standing height, swimming/gliding/riptide height]
|
||||
default:
|
||||
return this.possibleEyeHeights[0]; // [standing height, sneaking height, swimming/gliding/riptide height]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class BlockPlace {
|
||||
private static final BlockFace[] BY_2D = new BlockFace[]{BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST};
|
||||
static final BlockFace[] BY_3D = new BlockFace[]{BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST};
|
||||
@Setter
|
||||
Vector3i blockPosition;
|
||||
public Vector3i blockPosition;
|
||||
@Getter
|
||||
InteractionHand hand;
|
||||
@Getter
|
||||
|
||||
@@ -3,12 +3,11 @@ package ac.grim.grimac.utils.collisions;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.movementtick.MovementTickerStrider;
|
||||
import ac.grim.grimac.utils.collisions.blocks.*;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicFence;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicPane;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicWall;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicCollisionFence;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicCollisionPane;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicCollisionWall;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityStrider;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
@@ -25,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
|
||||
@@ -71,9 +71,7 @@ public enum CollisionData {
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}, StateTypes.LAVA),
|
||||
|
||||
WATER(NoCollisionBox.INSTANCE, StateTypes.WATER),
|
||||
|
||||
BREWINGSTAND((player, version, block, x, y, z) -> {
|
||||
BREWING_STAND((player, version, block, x, y, z) -> {
|
||||
int base = 0;
|
||||
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13))
|
||||
@@ -86,27 +84,13 @@ public enum CollisionData {
|
||||
}, StateTypes.BREWING_STAND),
|
||||
|
||||
BAMBOO((player, version, block, x, y, z) -> {
|
||||
// ViaVersion replacement block - sugarcane
|
||||
if (version.isOlderThan(ClientVersion.V_1_13_2))
|
||||
// ViaVersion replacement, sugarcane
|
||||
if (version.isOlderThan(ClientVersion.V_1_14)) {
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
// Offset taken from NMS
|
||||
long i = (x * 3129871L) ^ (long) z * 116129781L ^ (long) 0;
|
||||
i = i * i * 42317861L + i * 11L;
|
||||
i = i >> 16;
|
||||
|
||||
return new HexCollisionBox(6.5D, 0.0D, 6.5D, 9.5D, 16.0D, 9.5D).offset((((i & 15L) / 15.0F) - 0.5D) * 0.5D, 0, (((i >> 8 & 15L) / 15.0F) - 0.5D) * 0.5D);
|
||||
}
|
||||
return new HexOffsetCollisionBox(block.getType(), 6.5D, 0.0D, 6.5D, 9.5D, 16.0D, 9.5D);
|
||||
}, StateTypes.BAMBOO),
|
||||
|
||||
|
||||
BAMBOO_SAPLING((player, version, block, x, y, z) -> {
|
||||
long i = (x * 3129871L) ^ (long) z * 116129781L ^ (long) 0;
|
||||
i = i * i * 42317861L + i * 11L;
|
||||
i = i >> 16;
|
||||
|
||||
return new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 12.0D, 12.0D).offset((((i & 15L) / 15.0F) - 0.5D) * 0.5D, 0, (((i >> 8 & 15L) / 15.0F) - 0.5D) * 0.5D);
|
||||
}, StateTypes.BAMBOO_SAPLING),
|
||||
|
||||
COMPOSTER((player, version, block, x, y, z) -> {
|
||||
double height = 0.125;
|
||||
|
||||
@@ -159,7 +143,7 @@ public enum CollisionData {
|
||||
}
|
||||
}, BlockTags.ANVIL.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL(new DynamicWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
|
||||
WALL(new DynamicCollisionWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
|
||||
|
||||
SLAB((player, version, data, x, y, z) -> {
|
||||
Type slabType = data.getTypeData();
|
||||
@@ -177,7 +161,7 @@ public enum CollisionData {
|
||||
SKULL(new SimpleCollisionBox(0.25F, 0.0F, 0.25F, 0.75F, 0.5F, 0.75F, false),
|
||||
StateTypes.CREEPER_HEAD, StateTypes.ZOMBIE_HEAD, StateTypes.DRAGON_HEAD, StateTypes.PLAYER_HEAD,
|
||||
StateTypes.SKELETON_SKULL, StateTypes.WITHER_SKELETON_SKULL, StateTypes.HEAVY_CORE),
|
||||
|
||||
|
||||
PIGLIN_HEAD(new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D), StateTypes.PIGLIN_HEAD),
|
||||
|
||||
// Overwrite previous SKULL enum for legacy, where head and wall skull isn't separate
|
||||
@@ -210,13 +194,6 @@ public enum CollisionData {
|
||||
}
|
||||
}, StateTypes.PIGLIN_WALL_HEAD),
|
||||
|
||||
BANNER(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D),
|
||||
BlockTags.BANNERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
CORAL_FAN((player, version, data, x, y, z) -> {
|
||||
return 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) -> {
|
||||
@@ -281,43 +258,31 @@ public enum CollisionData {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D);
|
||||
}, StateTypes.STONECUTTER),
|
||||
|
||||
SWEET_BERRY((player, version, data, x, y, z) -> {
|
||||
if (data.getAge() == 0) {
|
||||
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D);
|
||||
} else if (data.getAge() < 3) {
|
||||
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
}
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}, StateTypes.SWEET_BERRY_BUSH),
|
||||
CORAL_FAN(NoCollisionBox.INSTANCE, BlockTags.CORALS.getStates().toArray(new StateType[0])),
|
||||
|
||||
SAPLING(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
|
||||
BlockTags.SAPLINGS.getStates().toArray(new StateType[0])),
|
||||
RAILS(NoCollisionBox.INSTANCE, BlockTags.RAILS.getStates().toArray(new StateType[0])),
|
||||
|
||||
ROOTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D),
|
||||
StateTypes.WARPED_ROOTS, StateTypes.CRIMSON_ROOTS),
|
||||
BANNER(NoCollisionBox.INSTANCE, BlockTags.BANNERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
FLOWER(new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D),
|
||||
BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
|
||||
SMALL_FLOWER(NoCollisionBox.INSTANCE, BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
DEAD_BUSH(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D), StateTypes.DEAD_BUSH),
|
||||
TALL_FLOWER(NoCollisionBox.INSTANCE, BlockTags.TALL_FLOWERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
SUGARCANE(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SUGAR_CANE),
|
||||
SAPLING(NoCollisionBox.INSTANCE, BlockTags.SAPLINGS.getStates().toArray(new StateType[0])),
|
||||
|
||||
NETHER_SPROUTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 3.0D, 14.0D), StateTypes.NETHER_SPROUTS),
|
||||
BUTTON(NoCollisionBox.INSTANCE, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
|
||||
|
||||
GRASS_FERN(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D),
|
||||
StateTypes.GRASS, StateTypes.FERN),
|
||||
|
||||
TALL_GRASS(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.TALL_GRASS),
|
||||
|
||||
SEA_GRASS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
|
||||
StateTypes.SEAGRASS),
|
||||
|
||||
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),
|
||||
|
||||
TWISTING_VINES(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D), StateTypes.TWISTING_VINES_PLANT, StateTypes.WEEPING_VINES_PLANT),
|
||||
// All states that unconditionally have no collision and are not in a group where every member also has no collision.
|
||||
NO_COLLISION(NoCollisionBox.INSTANCE, StateTypes.TWISTING_VINES_PLANT, StateTypes.WEEPING_VINES_PLANT,
|
||||
StateTypes.TWISTING_VINES, StateTypes.WEEPING_VINES, StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT,
|
||||
StateTypes.TALL_SEAGRASS, StateTypes.SEAGRASS, StateTypes.SHORT_GRASS, StateTypes.FERN, StateTypes.NETHER_SPROUTS,
|
||||
StateTypes.DEAD_BUSH, StateTypes.SUGAR_CANE, StateTypes.SWEET_BERRY_BUSH, StateTypes.WARPED_ROOTS,
|
||||
StateTypes.CRIMSON_ROOTS, StateTypes.TORCHFLOWER_CROP, StateTypes.PINK_PETALS, StateTypes.TALL_GRASS,
|
||||
StateTypes.LARGE_FERN, StateTypes.BAMBOO_SAPLING, StateTypes.HANGING_ROOTS,
|
||||
StateTypes.SMALL_DRIPLEAF, StateTypes.END_PORTAL, StateTypes.LEVER, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM,
|
||||
StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM, StateTypes.BEETROOTS, StateTypes.POTATOES,
|
||||
StateTypes.WHEAT, StateTypes.CARROTS, StateTypes.NETHER_WART, StateTypes.MOVING_PISTON, StateTypes.AIR, StateTypes.CAVE_AIR,
|
||||
StateTypes.VOID_AIR, StateTypes.LIGHT, StateTypes.WATER),
|
||||
|
||||
KELP(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D), StateTypes.KELP),
|
||||
// Kelp block is a full block, so it by default is correct
|
||||
@@ -445,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) -> {
|
||||
@@ -567,7 +505,7 @@ public enum CollisionData {
|
||||
|
||||
}, StateTypes.GRINDSTONE),
|
||||
|
||||
PANE(new DynamicPane(), Materials.getPanes().toArray(new StateType[0])),
|
||||
PANE(new DynamicCollisionPane(), Materials.getPanes().toArray(new StateType[0])),
|
||||
|
||||
CHAIN_BLOCK((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_16)) {
|
||||
@@ -604,7 +542,7 @@ public enum CollisionData {
|
||||
|
||||
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
|
||||
|
||||
FENCE(new DynamicFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
|
||||
FENCE(new DynamicCollisionFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
|
||||
|
||||
SNOW((player, version, data, x, y, z) -> {
|
||||
if (data.getLayers() == 1 && version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
@@ -682,8 +620,6 @@ public enum CollisionData {
|
||||
|
||||
}, StateTypes.FARMLAND),
|
||||
|
||||
HANGING_ROOTS(new HexCollisionBox(2.0D, 10.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.HANGING_ROOTS),
|
||||
|
||||
GRASS_PATH((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_9))
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 15.0D, 16.0D);
|
||||
@@ -746,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,
|
||||
@@ -829,71 +784,18 @@ 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])),
|
||||
|
||||
BEETROOT((player, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, (data.getAge() + 1) * 2, 1.0D);
|
||||
}, StateTypes.BEETROOTS),
|
||||
|
||||
WHEAT((player, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, (data.getAge() + 1) * 2, 1.0D);
|
||||
}, StateTypes.WHEAT),
|
||||
|
||||
CARROT_NETHERWART((player, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, data.getAge() + 2, 1.0D);
|
||||
}, StateTypes.CARROTS, StateTypes.NETHER_WART),
|
||||
|
||||
NETHER_WART((player, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 5 + (data.getAge() * 3), 1.0D);
|
||||
}, StateTypes.NETHER_WART),
|
||||
|
||||
BUTTON((player, version, data, x, y, z) -> {
|
||||
double f2 = (float) (data.isPowered() ? 1 : 2) / 16.0;
|
||||
|
||||
switch (data.getFacing()) {
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(0.0, 0.375, 0.3125, f2, 0.625, 0.6875, false);
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(1.0 - f2, 0.375, 0.3125, 1.0, 0.625, 0.6875, false);
|
||||
case NORTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.375, 0.0, 0.6875, 0.625, f2, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.375, 1.0 - f2, 0.6875, 0.625, 1.0, false);
|
||||
case DOWN:
|
||||
return new SimpleCollisionBox(0.3125, 0.0, 0.375, 0.6875, 0.0 + f2, 0.625, false);
|
||||
case UP:
|
||||
return new SimpleCollisionBox(0.3125, 1.0 - f2, 0.375, 0.6875, 1.0, 0.625, false);
|
||||
}
|
||||
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
|
||||
|
||||
LEVER((player, version, data, x, y, z) -> {
|
||||
double f = 0.1875;
|
||||
|
||||
switch (data.getFacing()) {
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(1.0 - f * 2.0, 0.2, 0.5 - f, 1.0, 0.8, 0.5 + f, false);
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.0, 0.2, 0.5 - f, f * 2.0, 0.8, 0.5 + f, false);
|
||||
case NORTH:
|
||||
return new SimpleCollisionBox(0.5 - f, 0.2, 1.0 - f * 2.0, 0.5 + f, 0.8, 1.0, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.5 - f, 0.2, 0.0, 0.5 + f, 0.8, f * 2.0, false);
|
||||
case DOWN:
|
||||
return new SimpleCollisionBox(0.25, 0.4, 0.25, 0.75, 1.0, 0.75, false);
|
||||
case UP:
|
||||
return new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 0.6, 0.75, false);
|
||||
}
|
||||
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
}, StateTypes.LEVER),
|
||||
|
||||
STONE_PRESSURE_PLATE((player, version, data, x, y, z) -> {
|
||||
if (data.isPowered()) { // Pressed
|
||||
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 0.5D, 15.0D);
|
||||
@@ -925,27 +827,6 @@ public enum CollisionData {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
|
||||
}, StateTypes.TRIPWIRE),
|
||||
|
||||
ATTACHED_PUMPKIN_STEM((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13))
|
||||
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
|
||||
switch (data.getFacing()) {
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
case NORTH:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
|
||||
case EAST:
|
||||
default:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
|
||||
}
|
||||
}, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM),
|
||||
|
||||
PUMPKIN_STEM((player, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(7, 0, 7, 9, 2 * (data.getAge() + 1), 9);
|
||||
}, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM),
|
||||
|
||||
TRIPWIRE_HOOK((player, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
@@ -980,20 +861,6 @@ public enum CollisionData {
|
||||
|
||||
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH),
|
||||
|
||||
RAILS((player, version, data, x, y, z) -> {
|
||||
Shape shape = data.getShape();
|
||||
if (shape == Shape.ASCENDING_EAST || shape == Shape.ASCENDING_WEST ||
|
||||
shape == Shape.ASCENDING_NORTH || shape == Shape.ASCENDING_SOUTH) {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
|
||||
}
|
||||
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
|
||||
|
||||
}, BlockTags.RAILS.getStates().toArray(new StateType[0])),
|
||||
|
||||
// Known as block 36 - has no collision box
|
||||
TECHNICAL_MOVING_PISTON(NoCollisionBox.INSTANCE, StateTypes.MOVING_PISTON),
|
||||
|
||||
// 1.17 blocks
|
||||
CANDLE((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_17)) {
|
||||
@@ -1022,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))
|
||||
@@ -1038,34 +911,29 @@ public enum CollisionData {
|
||||
|
||||
}, StateTypes.BIG_DRIPLEAF),
|
||||
|
||||
DRIPSTONE((player, version, data, x, y, z) -> {
|
||||
POINTED_DRIPSTONE((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_17))
|
||||
return getEndRod(version, BlockFace.UP);
|
||||
|
||||
HexCollisionBox box;
|
||||
SimpleCollisionBox box;
|
||||
|
||||
if (data.getThickness() == Thickness.TIP_MERGE) {
|
||||
box = new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 16.0, 11.0);
|
||||
box = new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 16.0, 11.0);
|
||||
} else if (data.getThickness() == Thickness.TIP) {
|
||||
if (data.getVerticalDirection() == VerticalDirection.DOWN) {
|
||||
box = new HexCollisionBox(5.0, 5.0, 5.0, 11.0, 16.0, 11.0);
|
||||
box = new HexOffsetCollisionBox(data.getType(), 5.0, 5.0, 5.0, 11.0, 16.0, 11.0);
|
||||
} else {
|
||||
box = new HexCollisionBox(5.0, 0.0, 5.0, 11.0, 11.0, 11.0);
|
||||
box = new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 11.0, 11.0);
|
||||
}
|
||||
} else if (data.getThickness() == Thickness.FRUSTUM) {
|
||||
box = new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0);
|
||||
box = new HexOffsetCollisionBox(data.getType(), 4.0, 0.0, 4.0, 12.0, 16.0, 12.0);
|
||||
} else if (data.getThickness() == Thickness.MIDDLE) {
|
||||
box = new HexCollisionBox(3.0, 0.0, 3.0, 13.0, 16.0, 13.0);
|
||||
box = new HexOffsetCollisionBox(data.getType(), 3.0, 0.0, 3.0, 13.0, 16.0, 13.0);
|
||||
} else {
|
||||
box = new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 16.0, 14.0);
|
||||
box = new HexOffsetCollisionBox(data.getType(), 2.0, 0.0, 2.0, 14.0, 16.0, 14.0);
|
||||
}
|
||||
|
||||
// Copied from NMS and it works! That's all you need to know.
|
||||
long i = (x * 3129871L) ^ (long) z * 116129781L ^ (long) 0;
|
||||
i = i * i * 42317861L + i * 11L;
|
||||
i = i >> 16;
|
||||
|
||||
return box.offset(GrimMath.clamp((((i & 15L) / 15.0F) - 0.5D) * 0.5D, -0.125f, 0.125f), 0, GrimMath.clamp((((i >> 8 & 15L) / 15.0F) - 0.5D) * 0.5D, -0.125f, 0.125f));
|
||||
return box;
|
||||
}, StateTypes.POINTED_DRIPSTONE),
|
||||
|
||||
POWDER_SNOW((player, version, data, x, y, z) -> {
|
||||
@@ -1092,8 +960,6 @@ public enum CollisionData {
|
||||
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D);
|
||||
}, StateTypes.NETHER_PORTAL),
|
||||
|
||||
END_PORTAL(new HexCollisionBox(0.0D, 6.0D, 0.0D, 16.0D, 12.0D, 16.0D), StateTypes.END_PORTAL),
|
||||
|
||||
AZALEA((player, version, data, x, y, z) -> {
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0, 8.0, 0.0, 16.0, 16.0, 16.0),
|
||||
new HexCollisionBox(6.0, 0.0, 6.0, 10.0, 8.0, 10.0));
|
||||
@@ -1141,30 +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),
|
||||
|
||||
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),
|
||||
|
||||
PINK_PETALS_BLOCK(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D), StateTypes.PINK_PETALS),
|
||||
|
||||
TORCHFLOWER_CROP((player, version, data, x, y, z) -> {
|
||||
if (data.getAge() == 0) {
|
||||
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
|
||||
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);
|
||||
}
|
||||
// age is 1
|
||||
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D);
|
||||
}, StateTypes.TORCHFLOWER_CROP),
|
||||
}, StateTypes.SCULK_SHRIEKER),
|
||||
|
||||
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),
|
||||
|
||||
@@ -1181,8 +1051,6 @@ public enum CollisionData {
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
NONE(NoCollisionBox.INSTANCE, StateTypes.AIR, StateTypes.CAVE_AIR, StateTypes.VOID_AIR, StateTypes.LIGHT),
|
||||
|
||||
DEFAULT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.STONE);
|
||||
|
||||
// This should be an array... but a hashmap will do for now...
|
||||
@@ -1254,7 +1122,7 @@ public enum CollisionData {
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
|
||||
private static CollisionBox getCocoa(ClientVersion version, int age, BlockFace direction) {
|
||||
public static CollisionBox getCocoa(ClientVersion version, int age, BlockFace direction) {
|
||||
// From 1.9 - 1.10, the large cocoa block is the same as the medium one
|
||||
// https://bugs.mojang.com/browse/MC-94274
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_9_1) && version.isOlderThan(ClientVersion.V_1_11))
|
||||
@@ -1323,7 +1191,7 @@ public enum CollisionData {
|
||||
// Would pre-computing all states be worth the memory cost? I doubt it
|
||||
public static CollisionData getData(StateType state) { // TODO: Find a better hack for lava and scaffolding
|
||||
// What the fuck mojang, why put noCollision() and then give PITCHER_CROP collision?
|
||||
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING || state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE || BlockTags.WALL_HANGING_SIGNS.contains(state) ? rawLookupMap.getOrDefault(state, DEFAULT) : NONE;
|
||||
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING || state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE || BlockTags.WALL_HANGING_SIGNS.contains(state) ? rawLookupMap.getOrDefault(state, DEFAULT) : NO_COLLISION;
|
||||
}
|
||||
|
||||
// TODO: This is wrong if a block doesn't have any hitbox and isn't specified, light block?
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package ac.grim.grimac.utils.collisions;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicFence;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicWall;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxFence;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxPane;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxWall;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
@@ -17,9 +18,510 @@ import java.util.*;
|
||||
|
||||
// Expansion to the CollisionData class, which is different than regular ray tracing hitboxes
|
||||
public enum HitboxData {
|
||||
|
||||
RAILS((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getShape()) {
|
||||
case ASCENDING_NORTH:
|
||||
case ASCENDING_SOUTH:
|
||||
case ASCENDING_EAST:
|
||||
case ASCENDING_WEST:
|
||||
if (version.isOlderThan(ClientVersion.V_1_8)) {
|
||||
StateType railType = data.getType();
|
||||
// Activator rails always appear as flat detector rails in 1.7.10 because of ViaVersion
|
||||
// Ascending power rails in 1.7 have flat rail hitbox https://bugs.mojang.com/browse/MC-9134
|
||||
if (railType == StateTypes.ACTIVATOR_RAIL || (railType == StateTypes.POWERED_RAIL && data.isPowered())) {
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F, false);
|
||||
}
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_9)) {
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
} else if (version.isNewerThanOrEquals(ClientVersion.V_1_9) && version.isOlderThan(ClientVersion.V_1_10)) {
|
||||
// https://bugs.mojang.com/browse/MC-89552 sloped rails in 1.9 - it is slightly taller than a regular rail
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F, false);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_11)) {
|
||||
// https://bugs.mojang.com/browse/MC-102638 All sloped rails are full blocks in 1.10
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
|
||||
default:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
|
||||
}
|
||||
}, BlockTags.RAILS.getStates().toArray(new StateType[0])),
|
||||
|
||||
END_PORTAL((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_9)) {
|
||||
return new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 0.0625D, 1.0D);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_17)) {
|
||||
return new SimpleCollisionBox(0.0, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D);
|
||||
}
|
||||
return new HexCollisionBox(0.0D, 6.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
||||
}, StateTypes.END_PORTAL),
|
||||
|
||||
FENCE_GATE((player, item, version, data, x, y, z) -> {
|
||||
// This technically should be taken from the block data/made multi-version/run block updates... but that's too far even for me
|
||||
// This way is so much easier and works unless the magic stick wand is used
|
||||
boolean isInWall;
|
||||
boolean isXAxis = data.getFacing() == BlockFace.WEST || data.getFacing() == BlockFace.EAST;
|
||||
if (isXAxis) {
|
||||
boolean zPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z + 1));
|
||||
boolean zNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z - 1));
|
||||
isInWall = zPosWall || zNegWall;
|
||||
} else {
|
||||
boolean xPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x + 1, y, z));
|
||||
boolean xNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x - 1, y, z));
|
||||
isInWall = xPosWall || xNegWall;
|
||||
}
|
||||
|
||||
if (isInWall) {
|
||||
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 13.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 13.0D, 10.0D);
|
||||
}
|
||||
|
||||
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 16.0D, 10.0D);
|
||||
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
|
||||
|
||||
|
||||
FENCE(new DynamicHitboxFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
|
||||
|
||||
PANE(new DynamicHitboxPane(), Materials.getPanes().toArray(new StateType[0])),
|
||||
|
||||
LEVER(((player, item, version, data, x, y, z) -> {
|
||||
Face face = data.getFace();
|
||||
BlockFace facing = data.getFacing();
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
double f = 0.1875;
|
||||
|
||||
switch (face) {
|
||||
case WALL:
|
||||
switch (facing) {
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(1.0 - f * 2.0, 0.2, 0.5 - f, 1.0, 0.8, 0.5 + f, false);
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.0, 0.2, 0.5 - f, f * 2.0, 0.8, 0.5 + f, false);
|
||||
case NORTH:
|
||||
return new SimpleCollisionBox(0.5 - f, 0.2, 1.0 - f * 2.0, 0.5 + f, 0.8, 1.0, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.5 - f, 0.2, 0.0, 0.5 + f, 0.8, f * 2.0, false);
|
||||
}
|
||||
case CEILING:
|
||||
return new SimpleCollisionBox(0.25, 0.4, 0.25, 0.75, 1.0, 0.75, false);
|
||||
case FLOOR:
|
||||
return new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 0.6, 0.75, false);
|
||||
}
|
||||
}
|
||||
|
||||
switch (face) {
|
||||
case FLOOR:
|
||||
// X-AXIS
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
|
||||
}
|
||||
// Z-AXIS
|
||||
return new SimpleCollisionBox(0.3125, 0.0, 0.25, 0.6875, 0.375, 0.75, false);
|
||||
case WALL:
|
||||
switch (facing) {
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.0, 0.25, 0.3125, 0.375, 0.75, 0.6875, false);
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(0.625, 0.25, 0.3125, 1.0, 0.75, 0.6875, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.25, 0.0, 0.6875, 0.75, 0.375, false);
|
||||
case NORTH:
|
||||
default:
|
||||
return new SimpleCollisionBox(0.3125, 0.25, 0.625, 0.6875, 0.75, 1.0, false);
|
||||
}
|
||||
case CEILING:
|
||||
default:
|
||||
// X-AXIS
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
|
||||
}
|
||||
// Z-Axis
|
||||
return new SimpleCollisionBox(0.3125, 0.625, 0.25, 0.6875, 1.0, 0.75, false);
|
||||
}
|
||||
}), StateTypes.LEVER),
|
||||
|
||||
BUTTON((player, item, version, data, x, y, z) -> {
|
||||
final Face face = data.getFace();
|
||||
final BlockFace facing = data.getFacing();
|
||||
final boolean powered = data.isPowered();
|
||||
|
||||
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
double f2 = (float) (data.isPowered() ? 1 : 2) / 16.0;
|
||||
|
||||
switch (face) {
|
||||
case WALL:
|
||||
switch (facing) {
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(0.0, 0.375, 0.3125, f2, 0.625, 0.6875, false);
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(1.0 - f2, 0.375, 0.3125, 1.0, 0.625, 0.6875, false);
|
||||
case NORTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.375, 0.0, 0.6875, 0.625, f2, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.375, 1.0 - f2, 0.6875, 0.625, 1.0, false);
|
||||
}
|
||||
case CEILING:
|
||||
return new SimpleCollisionBox(0.3125, 1.0 - f2, 0.375, 0.6875, 1.0, 0.625, false);
|
||||
case FLOOR:
|
||||
return new SimpleCollisionBox(0.3125, 0.0, 0.375, 0.6875, 0.0 + f2, 0.625, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch (face) {
|
||||
case WALL:
|
||||
CollisionBox shape;
|
||||
switch (facing) {
|
||||
case EAST:
|
||||
shape = powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
|
||||
break;
|
||||
case WEST:
|
||||
shape = powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
|
||||
break;
|
||||
case SOUTH:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
|
||||
break;
|
||||
case NORTH:
|
||||
case UP:
|
||||
case DOWN:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
|
||||
break;
|
||||
default:
|
||||
shape = NoCollisionBox.INSTANCE;
|
||||
}
|
||||
|
||||
return shape;
|
||||
case CEILING:
|
||||
// ViaVersion shows lever
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
|
||||
} else {
|
||||
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
|
||||
}
|
||||
case FLOOR:
|
||||
// ViaVersion shows lever
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
|
||||
}
|
||||
|
||||
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
|
||||
default:
|
||||
throw new IllegalStateException("Impossible Hitbox State");
|
||||
}
|
||||
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL(new DynamicHitboxWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_SIGN((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST:
|
||||
return new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
default:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
STANDING_SIGN((player, item, version, data, x, y, z) ->
|
||||
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
|
||||
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
SAPLING(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
|
||||
BlockTags.SAPLINGS.getStates().toArray(new StateType[0])),
|
||||
|
||||
ROOTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D),
|
||||
StateTypes.WARPED_ROOTS, StateTypes.CRIMSON_ROOTS),
|
||||
|
||||
BANNER(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D),
|
||||
StateTypes.WHITE_BANNER, StateTypes.ORANGE_BANNER, StateTypes.MAGENTA_BANNER, StateTypes.LIGHT_BLUE_BANNER,
|
||||
StateTypes.YELLOW_BANNER, StateTypes.LIME_BANNER, StateTypes.PINK_BANNER, StateTypes.GRAY_BANNER,
|
||||
StateTypes.LIGHT_GRAY_BANNER, StateTypes.CYAN_BANNER, StateTypes.PURPLE_BANNER, StateTypes.BLUE_BANNER,
|
||||
StateTypes.BROWN_BANNER, StateTypes.GREEN_BANNER, StateTypes.RED_BANNER, StateTypes.BLACK_BANNER),
|
||||
|
||||
WALL_BANNER((player, item, version, data, x, y, z) -> {
|
||||
// ViaVersion replacement block
|
||||
if (version.isOlderThan(ClientVersion.V_1_8)) {
|
||||
return WALL_SIGN.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}
|
||||
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0, 0.0, 14.0, 16.0, 12.5, 16.0);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0, 0.0, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST:
|
||||
return new HexCollisionBox(14.0, 0.0, 0.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 12.5, 2.0);
|
||||
default:
|
||||
throw new IllegalStateException("Impossible Banner Facing State; Something very wrong is going on");
|
||||
}
|
||||
}, StateTypes.WHITE_WALL_BANNER, StateTypes.ORANGE_WALL_BANNER, StateTypes.MAGENTA_WALL_BANNER,
|
||||
StateTypes.LIGHT_BLUE_WALL_BANNER, StateTypes.YELLOW_WALL_BANNER, StateTypes.LIME_WALL_BANNER,
|
||||
StateTypes.PINK_WALL_BANNER, StateTypes.GRAY_WALL_BANNER, StateTypes.LIGHT_GRAY_WALL_BANNER,
|
||||
StateTypes.CYAN_WALL_BANNER, StateTypes.PURPLE_WALL_BANNER, StateTypes.BLUE_WALL_BANNER,
|
||||
StateTypes.BROWN_WALL_BANNER, StateTypes.GREEN_WALL_BANNER, StateTypes.RED_WALL_BANNER, StateTypes.BLACK_WALL_BANNER),
|
||||
|
||||
BREWING_STAND((player, item, version, block, x, y, z) -> {
|
||||
// BEWARE OF https://bugs.mojang.com/browse/MC-85109 FOR 1.8 PLAYERS
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
|
||||
} else {
|
||||
return new ComplexCollisionBox(
|
||||
new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 2.0, 15.0),
|
||||
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
|
||||
}
|
||||
}, StateTypes.BREWING_STAND),
|
||||
|
||||
SMALL_FLOWER((player, item, version, data, x, y, z) -> player.getClientVersion().isOlderThan(ClientVersion.V_1_13)
|
||||
? new SimpleCollisionBox(0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D)
|
||||
: new OffsetCollisionBox(data.getType(), 0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D),
|
||||
BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
TALL_FLOWERS(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), BlockTags.TALL_FLOWERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
FIRE((player, item, version, data, x, y, z) -> {
|
||||
// Since 1.16 fire has a small hitbox
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
|
||||
}
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
|
||||
|
||||
HONEY_BLOCK(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.HONEY_BLOCK),
|
||||
|
||||
POWDER_SNOW(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.POWDER_SNOW),
|
||||
|
||||
SOUL_SAND(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SOUL_SAND),
|
||||
|
||||
CACTUS((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
// https://bugs.mojang.com/browse/MC-59610
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
}, StateTypes.CACTUS),
|
||||
|
||||
SNOW((player, item, version, data, x, y, z) -> {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, data.getLayers() * 0.125, 1);
|
||||
}, StateTypes.SNOW),
|
||||
|
||||
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
|
||||
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
|
||||
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
|
||||
|
||||
if (data.getFacing() == BlockFace.WEST) {
|
||||
common.add(new HexCollisionBox(1.0D, 10.0D, 0.0D, 5.333333D, 14.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(5.333333D, 12.0D, 0.0D, 9.666667D, 16.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(9.666667D, 14.0D, 0.0D, 14.0D, 18.0D, 16.0D));
|
||||
} else if (data.getFacing() == BlockFace.NORTH) {
|
||||
common.add(new HexCollisionBox(0.0D, 10.0D, 1.0D, 16.0D, 14.0D, 5.333333D));
|
||||
common.add(new HexCollisionBox(0.0D, 12.0D, 5.333333D, 16.0D, 16.0D, 9.666667D));
|
||||
common.add(new HexCollisionBox(0.0D, 14.0D, 9.666667D, 16.0D, 18.0D, 14.0D));
|
||||
} else if (data.getFacing() == BlockFace.EAST) {
|
||||
common.add(new HexCollisionBox(10.666667D, 10.0D, 0.0D, 15.0D, 14.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(6.333333D, 12.0D, 0.0D, 10.666667D, 16.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(2.0D, 14.0D, 0.0D, 6.333333D, 18.0D, 16.0D));
|
||||
} else { // SOUTH
|
||||
common.add(new HexCollisionBox(0.0D, 10.0D, 10.666667D, 16.0D, 14.0D, 15.0D));
|
||||
common.add(new HexCollisionBox(0.0D, 12.0D, 6.333333D, 16.0D, 16.0D, 10.666667D));
|
||||
common.add(new HexCollisionBox(0.0D, 14.0D, 2.0D, 16.0D, 18.0D, 6.333333D));
|
||||
}
|
||||
|
||||
return common;
|
||||
}, StateTypes.LECTERN),
|
||||
|
||||
GLOW_LICHEN_SCULK_VEIN((player, item, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
|
||||
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;
|
||||
} 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) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, (data.getAge() + 1) * 2, 16.0D);
|
||||
}, StateTypes.WHEAT, StateTypes.BEETROOTS),
|
||||
|
||||
CARROT_POTATOES((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, data.getAge() + 2, 16.0D);
|
||||
}, StateTypes.CARROTS, StateTypes.POTATOES),
|
||||
|
||||
NETHER_WART((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0, 5 + (data.getAge() * 3), 16.0D);
|
||||
}, StateTypes.NETHER_WART),
|
||||
|
||||
ATTACHED_PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13))
|
||||
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
|
||||
switch (data.getFacing()) {
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
|
||||
case WEST:
|
||||
return new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
case NORTH:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
|
||||
case EAST:
|
||||
default:
|
||||
return new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
|
||||
}
|
||||
}, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM),
|
||||
|
||||
PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(7, 0, 7, 9, 2 * (data.getAge() + 1), 9);
|
||||
}, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM),
|
||||
|
||||
// Hitbox/Outline is Same as Collision
|
||||
COCOA_BEANS((player, item, version, data, x, y, z) -> {
|
||||
return CollisionData.getCocoa(version, data.getAge(), data.getFacing());
|
||||
}, StateTypes.COCOA),
|
||||
|
||||
|
||||
// Easier to just use no collision box
|
||||
// Redstone wire is very complex with its collision shapes and has many de-syncs
|
||||
REDSTONE_WIRE(NoCollisionBox.INSTANCE, StateTypes.REDSTONE_WIRE),
|
||||
|
||||
SWEET_BERRY((player, item, version, data, x, y, z) -> {
|
||||
if (data.getAge() == 0) {
|
||||
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D);
|
||||
} else if (data.getAge() < 3) {
|
||||
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
}
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}, StateTypes.SWEET_BERRY_BUSH),
|
||||
|
||||
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) {
|
||||
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
|
||||
}
|
||||
// age is 1
|
||||
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D);
|
||||
}, StateTypes.TORCHFLOWER_CROP),
|
||||
|
||||
DEAD_BUSH(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D), StateTypes.DEAD_BUSH),
|
||||
|
||||
SUGARCANE(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SUGAR_CANE),
|
||||
|
||||
NETHER_SPROUTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 3.0D, 14.0D), StateTypes.NETHER_SPROUTS),
|
||||
|
||||
HANGING_ROOTS(new HexCollisionBox(2.0D, 10.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.HANGING_ROOTS),
|
||||
|
||||
GRASS_FERN((player, item, version, data, x ,y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
return new SimpleCollisionBox(0.1F, 0.0F, 0.1F, 0.9F, 0.8F, 0.9F);
|
||||
}
|
||||
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D);
|
||||
}, StateTypes.SHORT_GRASS, StateTypes.FERN),
|
||||
|
||||
SEA_GRASS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
|
||||
StateTypes.SEAGRASS),
|
||||
|
||||
TALL_SEAGRASS(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 16.0, 14.0),
|
||||
StateTypes.TALL_SEAGRASS),
|
||||
|
||||
SMALL_DRIPLEAF(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 13.0, 14.0), StateTypes.SMALL_DRIPLEAF),
|
||||
|
||||
CAVE_VINES(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT),
|
||||
|
||||
// Then your enum entries become:
|
||||
TWISTING_VINES_BLOCK((player, item, version, data, x, y, z) ->
|
||||
getVineCollisionBox(version, false, true), StateTypes.TWISTING_VINES),
|
||||
|
||||
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),
|
||||
|
||||
BAMBOO((player, item, version, data, x, y, z) -> data.getLeaves() == Leaves.LARGE
|
||||
? new HexOffsetCollisionBox(data.getType(), 3.0, 0.0, 3.0, 13.0, 16.0, 13.0)
|
||||
: new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 16.0, 11.0), StateTypes.BAMBOO),
|
||||
|
||||
BAMBOO_SAPLING((player, item, version, data, x, y, z) -> {
|
||||
return new HexOffsetCollisionBox(data.getType(), 4.0D, 0.0D, 4.0D, 12.0D, 12.0D, 12.0D);
|
||||
}, 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);
|
||||
}
|
||||
|
||||
@@ -67,222 +569,54 @@ public enum HitboxData {
|
||||
|
||||
}, StateTypes.BIG_DRIPLEAF),
|
||||
|
||||
FENCE_GATE((player, item, version, data, x, y, z) -> {
|
||||
// This technically should be taken from the block data/made multi-version/run block updates... but that's too far even for me
|
||||
// This way is so much easier and works unless the magic stick wand is used
|
||||
boolean isInWall;
|
||||
boolean isXAxis = data.getFacing() == BlockFace.WEST || data.getFacing() == BlockFace.EAST;
|
||||
if (isXAxis) {
|
||||
boolean zPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z + 1));
|
||||
boolean zNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x, y, z - 1));
|
||||
isInWall = zPosWall || zNegWall;
|
||||
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 {
|
||||
boolean xPosWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x + 1, y, z));
|
||||
boolean xNegWall = Materials.isWall(player.compensatedWorld.getStateTypeAt(x - 1, y, z));
|
||||
isInWall = xPosWall || xNegWall;
|
||||
return new HexOffsetCollisionBox(data.getType(), 7.0, getPropaguleMinHeight(data.getAge()), 7.0, 9.0, 16.0, 9.0);
|
||||
}
|
||||
}), StateTypes.MANGROVE_PROPAGULE),
|
||||
|
||||
if (isInWall) {
|
||||
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 13.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 13.0D, 10.0D);
|
||||
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),
|
||||
|
||||
return isXAxis ? new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D) : new HexCollisionBox(0.0D, 0.0D, 6.0D, 16.0D, 16.0D, 10.0D);
|
||||
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
|
||||
|
||||
|
||||
FENCE((player, item, version, data, x, y, z) -> {
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
|
||||
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_12_2)) {
|
||||
int i = 0;
|
||||
if (data.getSouth() == South.TRUE) {
|
||||
i |= 0b1;
|
||||
}
|
||||
if (data.getWest() == West.TRUE) {
|
||||
i |= 0b10;
|
||||
}
|
||||
if (data.getNorth() == North.TRUE) {
|
||||
i |= 0b100;
|
||||
}
|
||||
if (data.getEast() == East.TRUE) {
|
||||
i |= 0b1000;
|
||||
}
|
||||
|
||||
return DynamicFence.LEGACY_BOUNDING_BOXES[i].copy();
|
||||
}
|
||||
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
CollisionData.getData(state.getType()).getMovementCollisionBox(player, version, state).downCast(boxes);
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
box.maxY = 1;
|
||||
}
|
||||
|
||||
return new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
|
||||
}, BlockTags.FENCES.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL((player, item, version, data, x, y, z) -> {
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
|
||||
return new DynamicWall().fetchRegularBox(player, state, version, x, y, z);
|
||||
}, BlockTags.WALLS.getStates().toArray(new StateType[0])),
|
||||
|
||||
HONEY_BLOCK(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.HONEY_BLOCK),
|
||||
|
||||
POWDER_SNOW(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.POWDER_SNOW),
|
||||
|
||||
SOUL_SAND(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SOUL_SAND),
|
||||
|
||||
CACTUS(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CACTUS),
|
||||
|
||||
SNOW((player, item, version, data, x, y, z) -> {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, data.getLayers() * 0.125, 1);
|
||||
}, StateTypes.SNOW),
|
||||
|
||||
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
|
||||
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
|
||||
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
|
||||
|
||||
if (data.getFacing() == BlockFace.WEST) {
|
||||
common.add(new HexCollisionBox(1.0D, 10.0D, 0.0D, 5.333333D, 14.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(5.333333D, 12.0D, 0.0D, 9.666667D, 16.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(9.666667D, 14.0D, 0.0D, 14.0D, 18.0D, 16.0D));
|
||||
} else if (data.getFacing() == BlockFace.NORTH) {
|
||||
common.add(new HexCollisionBox(0.0D, 10.0D, 1.0D, 16.0D, 14.0D, 5.333333D));
|
||||
common.add(new HexCollisionBox(0.0D, 12.0D, 5.333333D, 16.0D, 16.0D, 9.666667D));
|
||||
common.add(new HexCollisionBox(0.0D, 14.0D, 9.666667D, 16.0D, 18.0D, 14.0D));
|
||||
} else if (data.getFacing() == BlockFace.EAST) {
|
||||
common.add(new HexCollisionBox(10.666667D, 10.0D, 0.0D, 15.0D, 14.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(6.333333D, 12.0D, 0.0D, 10.666667D, 16.0D, 16.0D));
|
||||
common.add(new HexCollisionBox(2.0D, 14.0D, 0.0D, 6.333333D, 18.0D, 16.0D));
|
||||
} else { // SOUTH
|
||||
common.add(new HexCollisionBox(0.0D, 10.0D, 10.666667D, 16.0D, 14.0D, 15.0D));
|
||||
common.add(new HexCollisionBox(0.0D, 12.0D, 6.333333D, 16.0D, 16.0D, 10.666667D));
|
||||
common.add(new HexCollisionBox(0.0D, 14.0D, 2.0D, 16.0D, 18.0D, 6.333333D));
|
||||
}
|
||||
|
||||
return common;
|
||||
}, StateTypes.LECTERN),
|
||||
|
||||
WALL_HANGING_SIGNS((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
case WEST:
|
||||
case EAST:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
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};
|
||||
|
||||
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()];
|
||||
}, StateTypes.PITCHER_CROP),
|
||||
|
||||
BUTTON((player, item, version, data, x, y, z) -> {
|
||||
final BlockFace facing = data.getFacing();
|
||||
final boolean powered = data.isPowered();
|
||||
switch (data.getFace()) {
|
||||
case FLOOR:
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
|
||||
}
|
||||
|
||||
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
|
||||
case WALL:
|
||||
CollisionBox shape;
|
||||
switch (facing) {
|
||||
case EAST:
|
||||
shape = powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
|
||||
break;
|
||||
case WEST:
|
||||
shape = powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
|
||||
break;
|
||||
case SOUTH:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
|
||||
break;
|
||||
case NORTH:
|
||||
case UP:
|
||||
case DOWN:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
|
||||
break;
|
||||
default:
|
||||
shape = NoCollisionBox.INSTANCE;
|
||||
}
|
||||
|
||||
return shape;
|
||||
case CEILING:
|
||||
default:
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
|
||||
} else {
|
||||
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
|
||||
}
|
||||
}
|
||||
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_SIGN((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST:
|
||||
return new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
default:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
STANDING_SIGN((player, item, version, data, x, y, z) ->
|
||||
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
|
||||
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
REDSTONE_WIRE((player, item, version, data, x, y, z) ->
|
||||
// Easier to just use no collision box
|
||||
// Redstone wire is very complex with its collision shapes and has many de-syncs
|
||||
NoCollisionBox.INSTANCE,
|
||||
StateTypes.REDSTONE_WIRE),
|
||||
|
||||
FIRE((player, item, version, data, x, y, z) -> {
|
||||
// Since 1.16 fire has a small hitbox
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
|
||||
}
|
||||
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
|
||||
|
||||
BANNER(((player, item, version, data, x, y, z) ->
|
||||
new SimpleCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0)),
|
||||
BlockTags.BANNERS.getStates().toArray(new StateType[0]));
|
||||
|
||||
// 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<>();
|
||||
|
||||
@@ -329,6 +663,59 @@ public enum HitboxData {
|
||||
return data.box.copy().offset(x, y, z);
|
||||
|
||||
// Allow this class to override collision boxes when they aren't the same as regular boxes
|
||||
return HitboxData.getData(block.getType()).dynamic.fetch(player, heldItem, version, block, x, y, z).offset(x, y, z);
|
||||
HitBoxFactory hitBoxFactory = data.dynamic;
|
||||
CollisionBox collisionBox = hitBoxFactory.fetch(player, heldItem, version, block, x, y, z);
|
||||
collisionBox.offset(x, y, z);
|
||||
return collisionBox;
|
||||
}
|
||||
|
||||
private static int getPropaguleMinHeight(int age) {
|
||||
switch (age) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
return 13 - age * 3;
|
||||
case 3:
|
||||
case 4:
|
||||
return (4 - age) * 3;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -18,11 +18,9 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicFence extends DynamicConnecting implements CollisionFactory {
|
||||
public class DynamicCollisionFence extends DynamicConnecting implements CollisionFactory {
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
|
||||
|
||||
public static SimpleCollisionBox[] LEGACY_BOUNDING_BOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
boolean east;
|
||||
+1
-1
@@ -19,7 +19,7 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicPane extends DynamicConnecting implements CollisionFactory {
|
||||
public class DynamicCollisionPane extends DynamicConnecting implements CollisionFactory {
|
||||
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
|
||||
|
||||
+5
-1
@@ -15,12 +15,16 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.South;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.enums.West;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
public class DynamicWall extends DynamicConnecting implements CollisionFactory {
|
||||
public class DynamicCollisionWall extends DynamicConnecting implements CollisionFactory {
|
||||
public static final CollisionBox[] BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
|
||||
// https://bugs.mojang.com/browse/MC-9565
|
||||
// https://bugs.mojang.com/browse/MC-94016
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false);
|
||||
|
||||
/**
|
||||
* @deprecated use DynamicHitboxWall
|
||||
*/
|
||||
@Deprecated
|
||||
public CollisionBox fetchRegularBox(GrimPlayer player, WrappedBlockState state, ClientVersion version, int x, int y, int z) {
|
||||
int north, south, west, east, up;
|
||||
north = south = west = east = up = 0;
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks.connecting;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.HitBoxFactory;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
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.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DynamicHitboxFence extends DynamicConnecting implements HitBoxFactory {
|
||||
private static final CollisionBox[] MODERN_HITBOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
|
||||
|
||||
public static SimpleCollisionBox[] LEGACY_HITBOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
|
||||
|
||||
static {
|
||||
for (int i = 0; i < MODERN_HITBOXES.length; i++) {
|
||||
CollisionBox collisionBox = MODERN_HITBOXES[i];
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
collisionBox.downCast(boxes);
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
if (box.maxY > 1) {
|
||||
box.maxY = 1;
|
||||
}
|
||||
}
|
||||
|
||||
MODERN_HITBOXES[i] = new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
boolean east;
|
||||
boolean north;
|
||||
boolean south;
|
||||
boolean west;
|
||||
|
||||
// 1.13+ servers on 1.13+ clients send the full fence data
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
|
||||
&& version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
east = block.getEast() != East.FALSE;
|
||||
north = block.getNorth() != North.FALSE;
|
||||
south = block.getSouth() != South.FALSE;
|
||||
west = block.getWest() != West.FALSE;
|
||||
} else {
|
||||
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
|
||||
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
|
||||
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
|
||||
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
|
||||
}
|
||||
|
||||
return version.isNewerThanOrEquals(ClientVersion.V_1_12_2)
|
||||
? getModernCollisionBox(north, east, south, west)
|
||||
: getLegacyCollisionBox(north, east, south, west);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
return LEGACY_HITBOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
}
|
||||
|
||||
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
return MODERN_HITBOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
|
||||
if (BlockTags.FENCES.contains(one))
|
||||
return !(one == StateTypes.NETHER_BRICK_FENCE) && !(two == StateTypes.NETHER_BRICK_FENCE);
|
||||
else
|
||||
return BlockTags.FENCES.contains(one) || CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks.connecting;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
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.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicHitboxPane extends DynamicConnecting implements HitBoxFactory {
|
||||
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType item, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
boolean east, north, south, west;
|
||||
|
||||
// 1.13+ servers on 1.13+ clients send the full fence data
|
||||
if (isModernVersion(version)) {
|
||||
east = block.getEast() != East.FALSE;
|
||||
north = block.getNorth() != North.FALSE;
|
||||
south = block.getSouth() != South.FALSE;
|
||||
west = block.getWest() != West.FALSE;
|
||||
} else {
|
||||
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
|
||||
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
|
||||
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
|
||||
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
|
||||
}
|
||||
|
||||
// On 1.7 and 1.8 clients, and 1.13+ clients on 1.7 and 1.8 servers, the glass pane is + instead of |
|
||||
if (shouldUseOldPaneShape(version, north, south, east, west)) {
|
||||
north = south = east = west = true;
|
||||
}
|
||||
|
||||
return version.isNewerThanOrEquals(ClientVersion.V_1_9)
|
||||
? getModernCollisionBox(north, east, south, west)
|
||||
: getLegacyCollisionBox(north, east, south, west);
|
||||
}
|
||||
|
||||
private boolean isModernVersion(ClientVersion version) {
|
||||
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
|
||||
&& version.isNewerThanOrEquals(ClientVersion.V_1_13);
|
||||
}
|
||||
|
||||
private boolean shouldUseOldPaneShape(ClientVersion version, boolean north, boolean south, boolean east, boolean west) {
|
||||
return (!north && !south && !east && !west) &&
|
||||
(version.isOlderThanOrEquals(ClientVersion.V_1_8) ||
|
||||
(PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_8_8) &&
|
||||
version.isNewerThanOrEquals(ClientVersion.V_1_13)));
|
||||
}
|
||||
|
||||
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
}
|
||||
|
||||
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
float minX = 0.4375F;
|
||||
float maxX = 0.5625F;
|
||||
float minZ = 0.4375F;
|
||||
float maxZ = 0.5625F;
|
||||
|
||||
if ((!west || !east) && (west || east || north || south)) {
|
||||
if (west) {
|
||||
minX = 0.0F;
|
||||
} else if (east) {
|
||||
maxX = 1.0F;
|
||||
}
|
||||
} else {
|
||||
minX = 0.0F;
|
||||
maxX = 1.0F;
|
||||
}
|
||||
|
||||
if ((!north || !south) && (west || east || north || south)) {
|
||||
if (north) {
|
||||
minZ = 0.0F;
|
||||
} else if (south) {
|
||||
maxZ = 1.0F;
|
||||
}
|
||||
} else {
|
||||
minZ = 0.0F;
|
||||
maxZ = 1.0F;
|
||||
}
|
||||
|
||||
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, 1.0F, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canConnectToGlassBlock() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
|
||||
if (BlockTags.GLASS_PANES.contains(one) || one == StateTypes.IRON_BARS) {
|
||||
return true;
|
||||
} else {
|
||||
return CollisionData.getData(one)
|
||||
.getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0)
|
||||
.isSideFullBlock(direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks.connecting;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
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.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
public class DynamicHitboxWall extends DynamicConnecting implements HitBoxFactory {
|
||||
private static final CollisionBox[] HIT_BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
|
||||
int[] connections = getConnections(player, version, state, x, y, z);
|
||||
int north = connections[0], south = connections[1], west = connections[2], east = connections[3], up = connections[4];
|
||||
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
return getModernHitBox(north, south, west, east, up);
|
||||
} else {
|
||||
return getLegacyHitBox(north, south, west, east);
|
||||
}
|
||||
}
|
||||
|
||||
private int[] getConnections(GrimPlayer player, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
|
||||
int north = 0, south = 0, west = 0, east = 0, up = 0;
|
||||
|
||||
if (isModernServer()) {
|
||||
boolean sixteen = PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_16);
|
||||
north = getConnectionValue(state.getNorth(), sixteen);
|
||||
east = getConnectionValue(state.getEast(), sixteen);
|
||||
south = getConnectionValue(state.getSouth(), sixteen);
|
||||
west = getConnectionValue(state.getWest(), sixteen);
|
||||
up = state.isUp() ? 1 : 0;
|
||||
} else {
|
||||
north = connectsTo(player, version, x, y, z, BlockFace.NORTH) ? 1 : 0;
|
||||
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH) ? 1 : 0;
|
||||
west = connectsTo(player, version, x, y, z, BlockFace.WEST) ? 1 : 0;
|
||||
east = connectsTo(player, version, x, y, z, BlockFace.EAST) ? 1 : 0;
|
||||
up = 1;
|
||||
}
|
||||
|
||||
return new int[]{north, south, west, east, up};
|
||||
}
|
||||
|
||||
private boolean isModernServer() {
|
||||
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_12_2);
|
||||
}
|
||||
|
||||
private int getConnectionValue(Enum<?> direction, boolean sixteen) {
|
||||
if (direction == North.NONE || direction == East.NONE || direction == South.NONE || direction == West.NONE) {
|
||||
return 0;
|
||||
}
|
||||
return (direction == North.LOW || direction == East.LOW || direction == South.LOW || direction == West.LOW || sixteen) ? 1 : 2;
|
||||
}
|
||||
|
||||
private CollisionBox getModernHitBox(int north, int south, int west, int east, int up) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox();
|
||||
if (up == 1) {
|
||||
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
|
||||
}
|
||||
|
||||
addDirectionalBox(box, north, 5, 0, 0.0D, 11, 14, 11);
|
||||
addDirectionalBox(box, south, 5, 0, 5, 11, 14, 16);
|
||||
addDirectionalBox(box, west, 0, 0, 5, 11, 14, 11);
|
||||
addDirectionalBox(box, east, 5, 0, 5, 16, 14, 11);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
private void addDirectionalBox(ComplexCollisionBox box, int direction, double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||
if (direction == 1) {
|
||||
box.add(new HexCollisionBox(x1, y1, z1, x2, y2, z2));
|
||||
} else if (direction == 2) {
|
||||
box.add(new HexCollisionBox(x1, y1, z1, x2, 16, z2));
|
||||
}
|
||||
}
|
||||
|
||||
private CollisionBox getLegacyHitBox(int north, int south, int west, int east) {
|
||||
float minX = 0.25F, maxX = 0.75F, minZ = 0.25F, maxZ = 0.75F;
|
||||
float maxY = 1.0F;
|
||||
|
||||
if (north == 1) minZ = 0.0F;
|
||||
if (south == 1) maxZ = 1.0F;
|
||||
if (west == 1) minX = 0.0F;
|
||||
if (east == 1) maxX = 1.0F;
|
||||
|
||||
if (north == 1 && south == 1 && west == 0 && east == 0) {
|
||||
maxY = 0.8125F;
|
||||
minX = 0.3125F;
|
||||
maxX = 0.6875F;
|
||||
} else if (west == 1 && east == 1 && north == 0 && south == 0) {
|
||||
maxY = 0.8125F;
|
||||
minZ = 0.3125F;
|
||||
maxZ = 0.6875F;
|
||||
}
|
||||
|
||||
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
|
||||
return BlockTags.WALLS.contains(one) ||
|
||||
CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -2,6 +2,18 @@ package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
public class HexCollisionBox extends SimpleCollisionBox {
|
||||
|
||||
/**
|
||||
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
|
||||
* Mojang's block hitbox values are all based on chunks, so they're stored in game as 16 * the actual size
|
||||
* When copying block hitbox values, it may be easier to simple copy the multiplied values and use this class
|
||||
* If your min/max values are < 1 you should probably check out {@link SimpleCollisionBox}
|
||||
* @param minX 16 * x position of first corner
|
||||
* @param minY 16 * y position of first corner
|
||||
* @param minZ 16 * z position of first corner
|
||||
* @param maxX 16 * x position of second corner
|
||||
* @param maxY 16 * y position of second corner
|
||||
* @param maxZ 16 * z position of second corner
|
||||
*/
|
||||
public HexCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
this.minX = minX / 16d;
|
||||
this.minY = minY / 16d;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
// Exists for the same reason as HexCollisionBox but for offset blocks; read comments there if unsure
|
||||
public class HexOffsetCollisionBox extends OffsetCollisionBox {
|
||||
public HexOffsetCollisionBox(StateType block, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
super(block, minX / 16d, minY / 16d, minZ / 16d, maxX / 16d, maxY / 16d, maxZ / 16d);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
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 java.util.HashSet;
|
||||
|
||||
public class OffsetCollisionBox extends SimpleCollisionBox {
|
||||
|
||||
public static enum OffsetType {
|
||||
NONE,
|
||||
XZ,
|
||||
XYZ,
|
||||
}
|
||||
|
||||
float maxHorizontalModelOffset = 0.25F;
|
||||
float maxVerticalModelOffset = 0.2F;
|
||||
double offsetX = 0;
|
||||
double offsetY = 0;
|
||||
double offsetZ = 0;
|
||||
|
||||
OffsetType offsetType;
|
||||
|
||||
private static final HashSet<StateType> XZ_OFFSET_BLOCKSTATES = new HashSet<>();
|
||||
private static final HashSet<StateType> XYZ_OFFSET_BLOCKSTATES = new HashSet<>();
|
||||
|
||||
static {
|
||||
// Can we add a hasOffSet to StateType() ?
|
||||
// Or a new BlockTag for XZ and XYZ Offset ?
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.MANGROVE_PROPAGULE);
|
||||
|
||||
XZ_OFFSET_BLOCKSTATES.addAll(BlockTags.SMALL_FLOWERS.getStates());
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.BAMBOO_SAPLING);
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.BAMBOO);
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.POINTED_DRIPSTONE);
|
||||
// Only offsets rendering HitBox on XZ // we should document this somewhere for future reference
|
||||
// XZ_OFFSET_BLOCKSTATES.addAll(BlockTags.TALL_FLOWERS.getStates());
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.TALL_SEAGRASS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.TALL_GRASS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.LARGE_FERN);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.WARPED_ROOTS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.NETHER_SPROUTS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.CRIMSON_ROOTS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.HANGING_ROOTS);
|
||||
|
||||
// Only offsets rendering on XYZ, not HitBox
|
||||
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.SHORT_GRASS);
|
||||
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.FERN);
|
||||
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.SMALL_DRIPLEAF);
|
||||
}
|
||||
|
||||
public OffsetCollisionBox(StateType block, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
super(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
if (block.equals(StateTypes.POINTED_DRIPSTONE)) {
|
||||
maxHorizontalModelOffset = 0.125F;
|
||||
}
|
||||
// else if (block.equals(StateTypes.SMALL_DRIPLEAF)) {
|
||||
// maxVerticalModelOffset = 0.1F;
|
||||
// }
|
||||
|
||||
if (XZ_OFFSET_BLOCKSTATES.contains(block)) {
|
||||
offsetType = OffsetType.XZ;
|
||||
return;
|
||||
} else if (XYZ_OFFSET_BLOCKSTATES.contains(block)) {
|
||||
offsetType = OffsetType.XYZ;
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Invalid State Type for OffSetCollisionBox: " + block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCollisionBox offset(double x, double y, double z) {
|
||||
// In case you want to call .offset() again or get the box values without offset.
|
||||
resetBlockStateOffSet();
|
||||
long l;
|
||||
switch (offsetType) {
|
||||
case NONE:
|
||||
return super.offset(x, y, z);
|
||||
case XZ:
|
||||
l = GrimMath.hashCode(x, 0, z);
|
||||
offsetX = GrimMath.clamp(((double)((float)(l & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
offsetZ = GrimMath.clamp(((double)((float)(l >> 8 & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
return super.offset(x + offsetX, y, z + offsetZ);
|
||||
case XYZ:
|
||||
l = GrimMath.hashCode(x, 0, z);
|
||||
offsetY = ((double)((float)(l >> 4 & 15L) / 15.0F) - 1.0) * (double) maxVerticalModelOffset;
|
||||
offsetX = GrimMath.clamp(((double)((float)(l & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
offsetZ = GrimMath.clamp(((double)((float)(l >> 8 & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
return super.offset(x + offsetX, offsetY, z + offsetZ);
|
||||
}
|
||||
// You *really* shouldn't be using this class if offsetType = null
|
||||
return null;
|
||||
}
|
||||
|
||||
public void resetBlockStateOffSet() {
|
||||
this.minX += offsetX;
|
||||
this.minY += offsetY;
|
||||
this.minZ += offsetZ;
|
||||
this.maxX += offsetX;
|
||||
this.maxY += offsetY;
|
||||
this.maxZ += offsetZ;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,17 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
this(0, 0, 0, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
|
||||
* If your min/max values are > 1 you should probably check out {@link HexCollisionBox}
|
||||
* @param minX x position of first corner
|
||||
* @param minY y position of first corner
|
||||
* @param minZ z position of first corner
|
||||
* @param maxX x position of second corner
|
||||
* @param maxY y position of second corner
|
||||
* @param maxZ z position of second corner
|
||||
* @param fullBlock - whether on not the box is a perfect 1x1x1 sized block
|
||||
*/
|
||||
public SimpleCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, boolean fullBlock) {
|
||||
this.minX = minX;
|
||||
this.maxX = maxX;
|
||||
@@ -47,7 +58,17 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
this(minX, minY, minZ, minX + 1, minY + 1, minZ + 1, true);
|
||||
}
|
||||
|
||||
// Use only if you don't know the fullBlock status, which is rare
|
||||
/**
|
||||
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
|
||||
* If your min/max values are > 1 you should probably check out {@link HexCollisionBox}
|
||||
* Use only if you don't know the fullBlock status, which is rare
|
||||
* @param minX x position of first corner
|
||||
* @param minY y position of first corner
|
||||
* @param minZ z position of first corner
|
||||
* @param maxX x position of second corner
|
||||
* @param maxY y position of second corner
|
||||
* @param maxZ z position of second corner
|
||||
*/
|
||||
public SimpleCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
this.minX = minX;
|
||||
this.maxX = maxX;
|
||||
@@ -142,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);
|
||||
|
||||
@@ -47,6 +47,28 @@ public class PacketEntitySelf extends PacketEntity {
|
||||
setAttribute(Attributes.GENERIC_STEP_HEIGHT, 0.5f);
|
||||
}
|
||||
|
||||
getAttribute(Attributes.GENERIC_SCALE).get().withSetRewriter((oldValue, newValue) -> {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_20_5) || (newValue).equals(oldValue)) {
|
||||
return oldValue;
|
||||
} else {
|
||||
// Elytra, standing, sneaking (1.14)
|
||||
player.possibleEyeHeights[2][0] = 0.4 * newValue;
|
||||
player.possibleEyeHeights[2][1] = 1.62 * newValue;
|
||||
player.possibleEyeHeights[2][2] = 1.27 * newValue;
|
||||
|
||||
// sneaking (1.14), standing, Elytra
|
||||
player.possibleEyeHeights[1][0] = 1.27 * newValue;
|
||||
player.possibleEyeHeights[1][1] = 1.62 * newValue;
|
||||
player.possibleEyeHeights[1][2] = 0.4 * newValue;
|
||||
|
||||
// standing, sneaking (1.14), Elytra
|
||||
player.possibleEyeHeights[0][0] = 1.62 * newValue;
|
||||
player.possibleEyeHeights[0][1] = 1.27 * newValue;
|
||||
player.possibleEyeHeights[0][2] = 0.4 * newValue;
|
||||
return newValue;
|
||||
}
|
||||
});
|
||||
|
||||
final ValuedAttribute movementSpeed = ValuedAttribute.ranged(Attributes.GENERIC_MOVEMENT_SPEED, 0.1f, 0, 1024);
|
||||
movementSpeed.with(new WrapperPlayServerUpdateAttributes.Property("MOVEMENT_SPEED", 0.1f, new ArrayList<>()));
|
||||
trackAttribute(movementSpeed);
|
||||
|
||||
@@ -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;
|
||||
@@ -105,4 +107,10 @@ public class GrimMath {
|
||||
public static boolean isNearlySame(double a, double b, double epoch) {
|
||||
return Math.abs(a-b) < epoch;
|
||||
}
|
||||
|
||||
public static long hashCode(double x, int y, double z) {
|
||||
long l = (long)(x * 3129871) ^ (long)z * 116129781L ^ (long)y;
|
||||
l = l * l * 42317861L + l * 11L;
|
||||
return l >> 16;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
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;
|
||||
import ac.grim.grimac.utils.data.HitData;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
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.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;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class BlockRayTrace {
|
||||
// Copied from MCP...
|
||||
// Returns null if there isn't anything.
|
||||
//
|
||||
// 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]);
|
||||
double endY = GrimMath.lerp(-1.0E-7D, end[1], start[1]);
|
||||
double endZ = GrimMath.lerp(-1.0E-7D, end[2], start[2]);
|
||||
double startX = GrimMath.lerp(-1.0E-7D, start[0], end[0]);
|
||||
double startY = GrimMath.lerp(-1.0E-7D, start[1], end[1]);
|
||||
double startZ = GrimMath.lerp(-1.0E-7D, start[2], end[2]);
|
||||
|
||||
int[] floorStart = new int[]{GrimMath.floor(startX), GrimMath.floor(startY), GrimMath.floor(startZ)};
|
||||
|
||||
if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) return null;
|
||||
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(floorStart[0], floorStart[1], floorStart[2]);
|
||||
Pair<int[], BlockFace> apply = predicate.apply(state, floorStart);
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
|
||||
double xDiff = endX - startX;
|
||||
double yDiff = endY - startY;
|
||||
double zDiff = endZ - startZ;
|
||||
double xSign = Math.signum(xDiff);
|
||||
double ySign = Math.signum(yDiff);
|
||||
double zSign = Math.signum(zDiff);
|
||||
|
||||
double posXInverse = xSign == 0 ? Double.MAX_VALUE : xSign / xDiff;
|
||||
double posYInverse = ySign == 0 ? Double.MAX_VALUE : ySign / yDiff;
|
||||
double posZInverse = zSign == 0 ? Double.MAX_VALUE : zSign / zDiff;
|
||||
|
||||
double tMaxX = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
|
||||
double tMaxY = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
|
||||
double tMaxZ = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
|
||||
|
||||
// tMax represents the maximum distance along each axis before crossing a block boundary
|
||||
// The loop continues as long as the ray hasn't reached its end point along at least one axis.
|
||||
// In each iteration, it moves to the next block boundary along the axis with the smallest tMax value,
|
||||
// updates the corresponding coordinate, and checks for a hit in the new block, Google "3D DDA" for more info
|
||||
while (tMaxX <= 1.0D || tMaxY <= 1.0D || tMaxZ <= 1.0D) {
|
||||
if (tMaxX < tMaxY) {
|
||||
if (tMaxX < tMaxZ) {
|
||||
floorStart[0] += xSign;
|
||||
tMaxX += posXInverse;
|
||||
} else {
|
||||
floorStart[2] += zSign;
|
||||
tMaxZ += posZInverse;
|
||||
}
|
||||
} else if (tMaxY < tMaxZ) {
|
||||
floorStart[1] += ySign;
|
||||
tMaxY += posYInverse;
|
||||
} else {
|
||||
floorStart[2] += zSign;
|
||||
tMaxZ += posZInverse;
|
||||
}
|
||||
|
||||
state = player.compensatedWorld.getWrappedBlockStateAt(floorStart[0], floorStart[1], floorStart[2]);
|
||||
apply = predicate.apply(state, floorStart);
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HitData traverseBlocks(GrimPlayer player, double[] start, double[] end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
|
||||
// I guess go back by the collision epsilon?
|
||||
double endX = GrimMath.lerp(-1.0E-7D, end[0], start[0]);
|
||||
double endY = GrimMath.lerp(-1.0E-7D, end[1], start[1]);
|
||||
double endZ = GrimMath.lerp(-1.0E-7D, end[2], start[2]);
|
||||
double startX = GrimMath.lerp(-1.0E-7D, start[0], end[0]);
|
||||
double startY = GrimMath.lerp(-1.0E-7D, start[1], end[1]);
|
||||
double startZ = GrimMath.lerp(-1.0E-7D, start[2], end[2]);
|
||||
int floorStartX = GrimMath.floor(startX);
|
||||
int floorStartY = GrimMath.floor(startY);
|
||||
int floorStartZ = GrimMath.floor(startZ);
|
||||
|
||||
if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) return null;
|
||||
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
|
||||
HitData apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
|
||||
double xDiff = endX - startX;
|
||||
double yDiff = endY - startY;
|
||||
double zDiff = endZ - startZ;
|
||||
double xSign = Math.signum(xDiff);
|
||||
double ySign = Math.signum(yDiff);
|
||||
double zSign = Math.signum(zDiff);
|
||||
|
||||
double posXInverse = xSign == 0 ? Double.MAX_VALUE : xSign / xDiff;
|
||||
double posYInverse = ySign == 0 ? Double.MAX_VALUE : ySign / yDiff;
|
||||
double posZInverse = zSign == 0 ? Double.MAX_VALUE : zSign / zDiff;
|
||||
|
||||
double tMaxX = posXInverse * (xSign > 0 ? 1.0D - GrimMath.frac(startX) : GrimMath.frac(startX));
|
||||
double tMaxY = posYInverse * (ySign > 0 ? 1.0D - GrimMath.frac(startY) : GrimMath.frac(startY));
|
||||
double tMaxZ = posZInverse * (zSign > 0 ? 1.0D - GrimMath.frac(startZ) : GrimMath.frac(startZ));
|
||||
|
||||
// tMax represents the maximum distance along each axis before crossing a block boundary
|
||||
// The loop continues as long as the ray hasn't reached its end point along at least one axis.
|
||||
// In each iteration, it moves to the next block boundary along the axis with the smallest tMax value,
|
||||
// updates the corresponding coordinate, and checks for a hit in the new block, Google "3D DDA" for more info
|
||||
while (tMaxX <= 1.0D || tMaxY <= 1.0D || tMaxZ <= 1.0D) {
|
||||
if (tMaxX < tMaxY) {
|
||||
if (tMaxX < tMaxZ) {
|
||||
floorStartX += xSign;
|
||||
tMaxX += posXInverse;
|
||||
} else {
|
||||
floorStartZ += zSign;
|
||||
tMaxZ += posZInverse;
|
||||
}
|
||||
} else if (tMaxY < tMaxZ) {
|
||||
floorStartY += ySign;
|
||||
tMaxY += posYInverse;
|
||||
} else {
|
||||
floorStartZ += zSign;
|
||||
tMaxZ += posZInverse;
|
||||
}
|
||||
|
||||
state = player.compensatedWorld.getWrappedBlockStateAt(floorStartX, floorStartY, floorStartZ);
|
||||
apply = predicate.apply(state, new Vector3i(floorStartX, floorStartY, floorStartZ));
|
||||
|
||||
if (apply != null) {
|
||||
return apply;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HitData traverseBlocks(GrimPlayer player, Vector3d start, Vector3d end, BiFunction<WrappedBlockState, Vector3i, HitData> predicate) {
|
||||
return traverseBlocks(player, new double[]{start.x, start.y, start.z}, new double[]{end.x, end.y, end.z}, predicate);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static HitData getNearestReachHitResult(GrimPlayer player, double[] startPos, double[] lookVec, double currentDistance, double maxDistance, int[] targetBlockVec, boolean raycastContext) {
|
||||
double[] endPos = new double[]{
|
||||
startPos[0] + lookVec[0] * maxDistance,
|
||||
startPos[1] + lookVec[1] * maxDistance,
|
||||
startPos[2] + lookVec[2] * maxDistance
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
double bestHitResult = Double.MAX_VALUE;
|
||||
double[] 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 (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));
|
||||
}
|
||||
|
||||
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, startPos, currentEnd);
|
||||
if (intercept.getFirst() == null) continue; // No intercept or wrong blockFace
|
||||
|
||||
double[] hitLoc = intercept.getFirst();
|
||||
|
||||
double distSq = distanceSquared(hitLoc, startPos);
|
||||
if (distSq < bestHitResult) {
|
||||
bestHitResult = distSq;
|
||||
bestHitLoc = hitLoc;
|
||||
bestFace = intercept.getSecond();
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
private static double distanceSquared(double[] vec1, double[] vec2) {
|
||||
double dx = vec1[0] - vec2[0];
|
||||
double dy = vec1[1] - vec2[1];
|
||||
double dz = vec1[2] - vec2[2];
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
public 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());
|
||||
Ray trace = new Ray(player, startingPos.getX(), startingPos.getY(), startingPos.getZ(), player.xRot, player.yRot);
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE);
|
||||
Vector endVec = trace.getPointAtDistance(distance);
|
||||
Vector3d endPos = new Vector3d(endVec.getX(), endVec.getY(), endVec.getZ());
|
||||
|
||||
return traverseBlocks(player, startingPos, endPos, (block, vector3i) -> {
|
||||
CollisionBox data = HitboxData.getBlockHitbox(player, heldItem, player.getClientVersion(), 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;
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
if (intercept.getFirst() == null) continue; // No intercept
|
||||
|
||||
Vector hitLoc = intercept.getFirst();
|
||||
|
||||
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
|
||||
bestHitResult = hitLoc.distanceSquared(startingVec);
|
||||
bestHitLoc = hitLoc;
|
||||
bestFace = intercept.getSecond();
|
||||
}
|
||||
}
|
||||
if (bestHitLoc != null) {
|
||||
return new HitData(vector3i, bestHitLoc, bestFace, block);
|
||||
}
|
||||
|
||||
if (sourcesHaveHitbox &&
|
||||
(player.compensatedWorld.isWaterSourceBlock(vector3i.getX(), vector3i.getY(), vector3i.getZ())
|
||||
|| player.compensatedWorld.getLavaFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ()) == (8 / 9f))) {
|
||||
double waterHeight = player.compensatedWorld.getFluidLevelAt(vector3i.getX(), vector3i.getY(), vector3i.getZ());
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(vector3i.getX(), vector3i.getY(), vector3i.getZ(), vector3i.getX() + 1, vector3i.getY() + waterHeight, vector3i.getZ() + 1);
|
||||
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
|
||||
if (intercept.getFirst() != null) {
|
||||
return new HitData(vector3i, intercept.getFirst(), intercept.getSecond(), block);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,8 @@ public class ReachUtils {
|
||||
|
||||
double lowest = Double.MAX_VALUE;
|
||||
|
||||
for (double eyes : player.getPossibleEyeHeights()) {
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
for (double eyes : possibleEyeHeights) {
|
||||
if (giveMovementThresholdLenience) targetBox.expand(player.getMovementThreshold());
|
||||
Vector from = new Vector(player.x, player.y + eyes, player.z);
|
||||
Vector closestPoint = VectorUtils.cutBoxToVector(from, targetBox);
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package ac.grim.grimac.utils.nmsutil;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
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 @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);
|
||||
double[] maxY = getIntermediateWithYValue(origin, end, self.maxY);
|
||||
double[] minZ = getIntermediateWithZValue(origin, end, self.minZ);
|
||||
double[] maxZ = getIntermediateWithZValue(origin, end, self.maxZ);
|
||||
|
||||
BlockFace bestFace = null;
|
||||
|
||||
if (!isVecInYZ(self, minX)) minX = null;
|
||||
if (!isVecInYZ(self, maxX)) maxX = null;
|
||||
if (!isVecInXZ(self, minY)) minY = null;
|
||||
if (!isVecInXZ(self, maxY)) maxY = null;
|
||||
if (!isVecInXY(self, minZ)) minZ = null;
|
||||
if (!isVecInXY(self, maxZ)) maxZ = null;
|
||||
|
||||
double[] best = null;
|
||||
double bestDistSq = Double.MAX_VALUE;
|
||||
|
||||
if (minX != null) {
|
||||
best = minX;
|
||||
bestDistSq = distanceSquared(origin, minX);
|
||||
bestFace = BlockFace.WEST;
|
||||
}
|
||||
|
||||
if (maxX != null) {
|
||||
double distSq = distanceSquared(origin, maxX);
|
||||
if (best == null || distSq < bestDistSq) {
|
||||
best = maxX;
|
||||
bestDistSq = distSq;
|
||||
bestFace = BlockFace.EAST;
|
||||
}
|
||||
}
|
||||
|
||||
if (minY != null) {
|
||||
double distSq = distanceSquared(origin, minY);
|
||||
if (best == null || distSq < bestDistSq) {
|
||||
best = minY;
|
||||
bestDistSq = distSq;
|
||||
bestFace = BlockFace.DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxY != null) {
|
||||
double distSq = distanceSquared(origin, maxY);
|
||||
if (best == null || distSq < bestDistSq) {
|
||||
best = maxY;
|
||||
bestDistSq = distSq;
|
||||
bestFace = BlockFace.UP;
|
||||
}
|
||||
}
|
||||
|
||||
if (minZ != null) {
|
||||
double distSq = distanceSquared(origin, minZ);
|
||||
if (best == null || distSq < bestDistSq) {
|
||||
best = minZ;
|
||||
bestDistSq = distSq;
|
||||
bestFace = BlockFace.NORTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxZ != null) {
|
||||
double distSq = distanceSquared(origin, maxZ);
|
||||
if (best == null || distSq < bestDistSq) {
|
||||
best = maxZ;
|
||||
bestFace = BlockFace.SOUTH;
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(best, bestFace);
|
||||
}
|
||||
|
||||
private static double[] getIntermediateWithXValue(double[] self, double[] other, double x) {
|
||||
double d0 = other[0] - self[0];
|
||||
double d1 = other[1] - self[1];
|
||||
double d2 = other[2] - self[2];
|
||||
|
||||
if (d0 * d0 < 1.0000000116860974E-7D) {
|
||||
return null;
|
||||
} else {
|
||||
double d3 = (x - self[0]) / d0;
|
||||
return d3 >= 0.0D && d3 <= 1.0D ? new double[]{self[0] + d0 * d3, self[1] + d1 * d3, self[2] + d2 * d3} : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static double[] getIntermediateWithYValue(double[] self, double[] other, double y) {
|
||||
double d0 = other[0] - self[0];
|
||||
double d1 = other[1] - self[1];
|
||||
double d2 = other[2] - self[2];
|
||||
|
||||
if (d1 * d1 < 1.0000000116860974E-7D) {
|
||||
return null;
|
||||
} else {
|
||||
double d3 = (y - self[1]) / d1;
|
||||
return d3 >= 0.0D && d3 <= 1.0D ? new double[]{self[0] + d0 * d3, self[1] + d1 * d3, self[2] + d2 * d3} : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static double[] getIntermediateWithZValue(double[] self, double[] other, double z) {
|
||||
double d0 = other[0] - self[0];
|
||||
double d1 = other[1] - self[1];
|
||||
double d2 = other[2] - self[2];
|
||||
|
||||
if (d2 * d2 < 1.0000000116860974E-7D) {
|
||||
return null;
|
||||
} else {
|
||||
double d3 = (z - self[2]) / d2;
|
||||
return d3 >= 0.0D && d3 <= 1.0D ? new double[]{self[0] + d0 * d3, self[1] + d1 * d3, self[2] + d2 * d3} : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isVecInYZ(SimpleCollisionBox self, double[] vec) {
|
||||
return vec != null && vec[1] >= self.minY && vec[1] <= self.maxY && vec[2] >= self.minZ && vec[2] <= self.maxZ;
|
||||
}
|
||||
|
||||
private static boolean isVecInXZ(SimpleCollisionBox self, double[] vec) {
|
||||
return vec != null && vec[0] >= self.minX && vec[0] <= self.maxX && vec[2] >= self.minZ && vec[2] <= self.maxZ;
|
||||
}
|
||||
|
||||
private static boolean isVecInXY(SimpleCollisionBox self, double[] vec) {
|
||||
return vec != null && vec[0] >= self.minX && vec[0] <= self.maxX && vec[1] >= self.minY && vec[1] <= self.maxY;
|
||||
}
|
||||
|
||||
public static boolean isVecInside(SimpleCollisionBox self, double[] vec) {
|
||||
return vec[0] > self.minX && vec[0] < self.maxX &&
|
||||
vec[1] > self.minY && vec[1] < self.maxY &&
|
||||
vec[2] > self.minZ && vec[2] < self.maxZ;
|
||||
}
|
||||
|
||||
private static double distanceSquared(double[] vec1, double[] vec2) {
|
||||
double dx = vec1[0] - vec2[0];
|
||||
double dy = vec1[1] - vec2[1];
|
||||
double dz = vec1[2] - vec2[2];
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
// The following methods remain unchanged but are included for completeness
|
||||
|
||||
public static double[] getLook(GrimPlayer player, float yaw, float pitch) {
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_12_2)) {
|
||||
float f = player.trigHandler.cos(-yaw * 0.017453292F - (float)Math.PI);
|
||||
float f1 = player.trigHandler.sin(-yaw * 0.017453292F - (float)Math.PI);
|
||||
float f2 = -player.trigHandler.cos(-pitch * 0.017453292F);
|
||||
float f3 = player.trigHandler.sin(-pitch * 0.017453292F);
|
||||
return new double[]{f1 * f2, f3, f * f2};
|
||||
} else {
|
||||
float f = pitch * ((float) Math.PI / 180F);
|
||||
float f1 = -yaw * ((float) Math.PI / 180F);
|
||||
float f2 = player.trigHandler.cos(f1);
|
||||
float f3 = player.trigHandler.sin(f1);
|
||||
float f4 = player.trigHandler.cos(f);
|
||||
float f5 = player.trigHandler.sin(f);
|
||||
return new double[]{f3 * f4, -f5, f2 * f4};
|
||||
}
|
||||
}
|
||||
|
||||
// public static double getMinReachToBox(GrimPlayer player, SimpleCollisionBox targetBox) {
|
||||
// boolean giveMovementThresholdLenience = player.packetStateData.didLastMovementIncludePosition || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9);
|
||||
// if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) targetBox.expand(0.1);
|
||||
//
|
||||
// double lowest = Double.MAX_VALUE;
|
||||
//
|
||||
// for (double eyes : player.getPossibleEyeHeights()) {
|
||||
// if (giveMovementThresholdLenience) targetBox.expand(player.getMovementThreshold());
|
||||
// double[] from = new double[]{player.x, player.y + eyes, player.z};
|
||||
// double[] closestPoint = VectorUtils.cutBoxToVector(from, targetBox);
|
||||
// lowest = Math.min(lowest, Math.sqrt(distanceSquared(from, closestPoint)));
|
||||
// }
|
||||
//
|
||||
// return lowest;
|
||||
// }
|
||||
}
|
||||
@@ -81,9 +81,53 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
# Prediction-based no slow check
|
||||
# Grim ist verantwortlich für den fehlerhaften Netcode hier... schwer zu falsen, selbst wenn man Rechtsklick und Offhand-Taste spammt.
|
||||
# Viel stabiler als andere Anticheats, aber bitte melde alle Falses... Ich habe hier eine Menge Netcode-Probleme behoben.
|
||||
|
||||
@@ -81,8 +81,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
|
||||
# Prediction-based no slow check
|
||||
# Grim accounts for buggy netcode here... hard to false even when spamming right click and offhand button
|
||||
|
||||
@@ -82,9 +82,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
# Comprobación de NoSlow basado en predicciones
|
||||
# Grim compensa por código de red con errores aquí... difícil de sacar un falso positivo incluso haciendo spam del
|
||||
# clic derecho y la mano secundaria.
|
||||
|
||||
@@ -81,9 +81,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
# Vérification de no-slow basée sur la prédiction.
|
||||
# Grim tient compte des problèmes de netcode ici... difficile à tromper même en spamant le clic droit et le bouton de la main secondaire.
|
||||
# Bien plus stable que les autres anti-triche, mais veuillez signaler toute fausse détection... J'ai corrigé de nombreux problèmes de netcode ici.
|
||||
|
||||
@@ -78,9 +78,52 @@ PositionPlace:
|
||||
# Livello di violazione per il piazzamento in una posizione specifica
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
# Livello di violazione per il piazzamento con rotazione specifica
|
||||
cancelvl: 5
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
|
||||
NoSlowA:
|
||||
# Limite per rilevare l'uso scorretto dell'oggetto
|
||||
|
||||
@@ -81,9 +81,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
# Op voorspelling gebaseerde NoSlow-controle
|
||||
# Grim is verantwoordelijk voor buggy netcode hier... moeilijk om vals te spelen, zelfs als je met rechts klikt en de offhand-knop spamt
|
||||
# Veel stabieler dan andere anticheats, maar rapporteer alsjeblieft alle falses... Ik heb hier een heleboel netcode-problemen opgelost.
|
||||
|
||||
@@ -82,9 +82,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
# Verificação de NoSlow por simulação.
|
||||
# Grim considera erros no netcode. Difícil de gerar falsos positivos, até mesmo spamando o blotão direito e a offhand.
|
||||
# Muito mais estável que outros anticheats, mas por favor, reporte quaisquer falsos positivos. Eu já arrumei diversos
|
||||
|
||||
@@ -81,9 +81,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
# Основанная на предсказаниях проверка на отсутствие замедления
|
||||
# Grim учитывает баги неткода здесь... трудно ошибиться, даже если спамить правым кликом и кнопкой оффхенд.
|
||||
# Намного стабильнее, чем другие античиты, но, пожалуйста, сообщайте о любых фейлах... Я исправил тонну проблем с неткодом.
|
||||
|
||||
@@ -81,8 +81,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
|
||||
# Tahmin tabanlı yavaşlama kontrolü
|
||||
# Grim burada hatalı ağ kodunu dikkate alır... sağ tıklama ve el yan tuşunu spam yapsanız bile yanlış pozitiflik oluşturması zordur
|
||||
|
||||
@@ -82,8 +82,52 @@ FarPlace:
|
||||
PositionPlace:
|
||||
cancelvl: 5
|
||||
|
||||
RotationPlace:
|
||||
cancelvl: 5
|
||||
LineOfSightPlace:
|
||||
cancelvl: 1
|
||||
# Determines whether to only run the LineOfSightPlace check on specific blocks
|
||||
# Increases performance, mainly intended for mini-game servers which already prevent block placement
|
||||
# And want to stop container or workstation access through walls
|
||||
use-block-whitelist: false
|
||||
block-whitelist:
|
||||
- ANVIL
|
||||
- BARREL
|
||||
- BEACON
|
||||
- BEEHIVE
|
||||
- BEE_NEST
|
||||
- BLAST_FURNACE
|
||||
- BREWING_STAND
|
||||
- CAMPFIRE
|
||||
- CARTOGRAPHY_TABLE
|
||||
- CHEST
|
||||
- CHEST_MINECART
|
||||
- CHIPPED_ANVIL
|
||||
- COMPOSTER
|
||||
- CRAFTING_TABLE
|
||||
- DAMAGED_ANVIL
|
||||
- DISPENSER
|
||||
- DROPPER
|
||||
- ENCHANTING_TABLE
|
||||
- ENDER_CHEST
|
||||
- FURNACE
|
||||
- FURNACE_MINECART
|
||||
- FLETCHING_TABLE
|
||||
- GRINDSTONE
|
||||
- HOPPER
|
||||
- HOPPER_MINECART
|
||||
- ITEM_FRAME
|
||||
- JUKEBOX
|
||||
- LECTERN
|
||||
- LOOM
|
||||
- RESPAWN_ANCHOR
|
||||
- SHULKER_BOX
|
||||
- SMITHING_TABLE
|
||||
- SMOKER
|
||||
- SOUL_CAMPFIRE
|
||||
- STONECUTTER
|
||||
- TRAPPED_CHEST
|
||||
- COMMAND_BLOCK
|
||||
- STRUCTURE_BLOCK
|
||||
- JIGSAW
|
||||
|
||||
# 使用模拟移动进行的NoSlow(使用物品时不会减速)检查
|
||||
# 在这里解释错误的服务器代码......即使在发送垃圾数据包时右键单击和副手按钮也很分辨
|
||||
|
||||
Reference in new issue
Block a user