Skip to content

Commit

Permalink
All HitBox and CollisionBox Changes made for LineOfSightPlace
Browse files Browse the repository at this point in the history
  • Loading branch information
Axionize committed Sep 17, 2024
1 parent 598caa9 commit 16bb09a
Show file tree
Hide file tree
Showing 10 changed files with 805 additions and 278 deletions.
201 changes: 45 additions & 156 deletions src/main/java/ac/grim/grimac/utils/collisions/CollisionData.java

Large diffs are not rendered by default.

537 changes: 417 additions & 120 deletions src/main/java/ac/grim/grimac/utils/collisions/HitboxData.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
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 in favor of DynamicHitboxWall
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

public class HexCollisionBox extends SimpleCollisionBox {

// Mojang's block hitbox values are all based on chunks, so they're stored in game as 16 * the actual values we want
// When copying block hitbox values, it may be easier to simple copy the multiplied values and divide them
// HexCollisionBox is a simple extension of SimpleCollisionBox that does this for us.
// If none of your min/max values are > 1 you probably should not be using this class.
public HexCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
this.minX = minX / 16d;
this.minY = minY / 16d;
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class SimpleCollisionBox implements CollisionBox {
public double minX, minY, minZ, maxX, maxY, maxZ;
private boolean isFullBlock = false;

// If your min/max values are > 1 you should probably check out HexCollisionBox
public SimpleCollisionBox() {
this(0, 0, 0, 0, 0, 0, false);
}
Expand Down

0 comments on commit 16bb09a

Please sign in to comment.