[BLEEDING] Switch implementation for moving.passable.

Switch to AxisTracing instead of RayTracing. This means most workarounds
are removed, including some (legacy) performance saving tweaks.
This commit is contained in:
asofold 2016-06-11 15:49:49 +02:00
parent ec333fb432
commit 2ff0076e69
4 changed files with 232 additions and 58 deletions

View File

@ -39,7 +39,7 @@ public class Passable extends Check {
// TODO: Make this configurable once a working set of settings has been found.
// TODO: Once made configurable... intense testing... and test cases.
private static boolean rt_legacy = true;
private static boolean rt_legacy = false;
// TODO: rt_xzFactor = 1.0; // Problems: Doors, fences.
private static double rt_xzFactor = 0.98;
// TODO: Test bumping head into things.
@ -68,56 +68,49 @@ public class Passable extends Check {
public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc)
{
// TODO: if (!from.isSameCoords(loc)) {...check passable for loc -> from !?... + sf etc too?}
// TODO: Future: Account for the players bounding box? [test very-strict setting for at least the end points...]
// TODO: WAT: if (!from.isSameCoords(loc)) {...check passable for loc -> from !?... + sf etc too?}
// TODO: Distinguish feet vs. box.
String tags = "";
// Block distances (sum, max) for from-to (not for loc!).
final int manhattan = from.manhattan(to);
// Skip moves inside of ignored blocks right away [works as long as we only check between foot-locations].
if (manhattan <= 1 && BlockProperties.isPassable(from.getTypeId())) {
// TODO: Monitor: BlockProperties.isPassable checks slightly different than before.
if (manhattan == 0){
return null;
} else {
// manhattan == 1
if (BlockProperties.isPassable(to.getTypeId())) {
if (rt_legacy) {
// Skip moves inside of ignored blocks right away [works as long as we only check between foot-locations].
if (manhattan <= 1 && BlockProperties.isPassable(from.getTypeId())) {
// TODO: Monitor: BlockProperties.isPassable checks slightly different than before.
if (manhattan == 0){
return null;
} else {
// manhattan == 1
if (BlockProperties.isPassable(to.getTypeId())) {
return null;
}
}
}
}
}
boolean toPassable = to.isPassable();
// General condition check for using ray-tracing.
if (toPassable && cc.passableRayTracingCheck && (!cc.passableRayTracingBlockChangeOnly || manhattan > 0)) {
setNormalMargins(rayTracing, from);
rayTracing.set(from, to);
rayTracing.loop();
if (rayTracing.collides() || rayTracing.getStepsDone() >= rayTracing.getMaxSteps()) {
if (data.debug) {
debugExtraCollisionDetails(player, rayTracing, "initial");
}
final int maxBlockDist = manhattan <= 1 ? manhattan : from.maxBlockDist(to);
if (maxBlockDist <= 1 && rayTracing.getStepsDone() == 1 && !from.isPassable()) {
// Redo ray-tracing for moving out of blocks.
if (collidesIgnoreFirst(from, to)) {
toPassable = false;
tags = "raytracing_2x_";
if (data.debug) {
debugExtraCollisionDetails(player, rayTracing, "ingoreFirst");
}
}
else if (data.debug) {
debug(player, "Allow moving out of a block.");
}
}
else{
if (!allowsSplitMove(from, to, manhattan, data)) {
toPassable = false;
tags = "raytracing_";
}
if ((!rt_legacy || toPassable) && cc.passableRayTracingCheck
&& (!cc.passableRayTracingBlockChangeOnly || manhattan > 0)) {
final String newTag;
if (rt_legacy) {
newTag = checkRayTracingLegacy(player, from, to, manhattan, data, cc);
if (newTag != null) {
toPassable = false;
tags = newTag;
}
}
else {
newTag = checkRayTracing(player, from, to, manhattan, data, cc);
if (newTag != null) {
// Direct return.
return potentialViolation(player, from, to, manhattan, tags, data, cc);
}
}
// TODO: Future: If accuracy is demanded, also check the head position (or bounding box right away).
rayTracing.cleanup();
}
// TODO: Checking order: If loc is not the same as from, a quick return here might not be wanted.
@ -127,11 +120,68 @@ public class Passable extends Check {
data.passableVL *= 0.99;
return null;
} else {
return potentialViolation(player, from, to, manhattan, tags, data, cc);
return potentialViolationLegacy(player, from, to, manhattan, tags, data, cc);
}
}
private String checkRayTracing(final Player player, final PlayerLocation from, final PlayerLocation to,
final int manhattan, final MovingData data, final MovingConfig cc) {
String tags = null;
setNormalMargins(rayTracing, from);
rayTracing.set(from, to);
rayTracing.setIgnoreInitiallyColliding(true);
//rayTracing.setCutOppositeDirectionMargin(true);
rayTracing.loop();
rayTracing.setIgnoreInitiallyColliding(false);
//rayTracing.setCutOppositeDirectionMargin(false);
if (rayTracing.collides()) {
tags = "raytracing_collide_";
}
else if (rayTracing.getStepsDone() >= rayTracing.getMaxSteps()) {
tags = "raytracing_maxsteps_";
}
if (data.debug) {
debugExtraCollisionDetails(player, rayTracing, "std");
}
rayTracing.cleanup();
return tags;
}
private String checkRayTracingLegacy(final Player player, final PlayerLocation from, final PlayerLocation to,
final int manhattan, final MovingData data, final MovingConfig cc) {
setNormalMargins(rayTracing, from);
rayTracing.set(from, to);
rayTracing.loop();
String tags = null;
if (rayTracing.collides() || rayTracing.getStepsDone() >= rayTracing.getMaxSteps()) {
if (data.debug) {
debugExtraCollisionDetails(player, rayTracing, "legacy");
}
final int maxBlockDist = manhattan <= 1 ? manhattan : from.maxBlockDist(to);
if (maxBlockDist <= 1 && rayTracing.getStepsDone() == 1 && !from.isPassable()) {
// Redo ray-tracing for moving out of blocks.
if (collidesIgnoreFirst(from, to)) {
tags = "raytracing_2x_";
if (data.debug) {
debugExtraCollisionDetails(player, rayTracing, "ingoreFirst");
}
}
else if (data.debug) {
debug(player, "Allow moving out of a block.");
}
}
else{
if (!allowsSplitMove(from, to, manhattan, data)) {
tags = "raytracing_";
}
}
}
// TODO: Future: If accuracy is demanded, also check the head position (or bounding box right away).
rayTracing.cleanup();
return tags;
}
/**
* Default/normal margins.
* @param rayTracing
@ -141,8 +191,44 @@ public class Passable extends Check {
rayTracing.setMargins(from.getEyeHeight() * rt_heightFactor, from.getWidth() / 2.0 * rt_xzFactor); // max from/to + resolution ?
}
private Location potentialViolation(final Player player, final PlayerLocation from, final PlayerLocation to, final int manhattan, String tags, final MovingData data, final MovingConfig cc) {
/**
* Axis-wise ray-tracing violation skipping conditions.
*
* @param player
* @param from
* @param to
* @param manhattan
* @param tags
* @param data
* @param cc
* @return
*/
private Location potentialViolation(final Player player,
final PlayerLocation from, final PlayerLocation to, final int manhattan,
String tags, final MovingData data, final MovingConfig cc) {
// TODO: Might need the workaround for fences.
return actualViolation(player, from, to, tags, data, cc);
}
/**
* Legacy skipping conditions, before triggering an actual violation.
*
* @param player
* @param from
* @param to
* @param manhattan
* @param tags
* @param data
* @param cc
* @return
*/
private Location potentialViolationLegacy(final Player player,
final PlayerLocation from, final PlayerLocation to, final int manhattan,
String tags, final MovingData data, final MovingConfig cc) {
// Moving into a block, possibly a violation.
// TODO: Do account for settings and ray-tracing here.
// First check if the player is moving from a passable location.
// If not, the move might still be allowed, if moving inside of the same block, or from and to have head position passable.
@ -163,21 +249,27 @@ public class Passable extends Check {
// (Mind that this can be the case on the same block theoretically.)
// Keep loc as set-back.
// }
else if (manhattan == 1 && to.isBlockAbove(from) && BlockProperties.isPassable(from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) {
else if (manhattan == 1 && to.isBlockAbove(from)
&& BlockProperties.isPassable(from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) {
// else if (to.isBlockAbove(from) && BlockProperties.isPassableExact(from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) {
// Allow the move up if the head is free.
// TODO: Better distinguish ray-tracing (through something thin) or check to-head-passable too?
return null;
}
else if (manhattan > 0) {
// Otherwise keep from as set-back.
tags += "cross";
}
else{
// All blocks are the same, allow the move.
else {
// manhattan == 0
// TODO: Even legacy ray-tracing will now account for actual initial collision.
return null;
}
return actualViolation(player, from, to, tags, data, cc);
}
private Location actualViolation(final Player player, final PlayerLocation from, final PlayerLocation to,
final String tags, final MovingData data, final MovingConfig cc) {
Location setBackLoc = null; // Alternative to from.getLocation().
// Prefer the set-back location from the data.
@ -188,8 +280,6 @@ public class Passable extends Check {
}
}
// TODO: set data.set-back ? or something: still some aji here.
// Return the reset position.
data.passableVL += 1d;
final ViolationData vd = new ViolationData(this, player, data.passableVL, 1, cc.passableActions);
@ -290,6 +380,13 @@ public class Passable extends Check {
return false;
}
/**
* Debug only if colliding.
*
* @param player
* @param rayTracing
* @param tag
*/
private void debugExtraCollisionDetails(Player player, ICollidePassable rayTracing, String tag) {
if (rayTracing.collides()) {
debug(player, "Raytracing collision (" + tag + "): " + rayTracing.getCollidingAxis());

View File

@ -3154,6 +3154,40 @@ public class BlockProperties {
return false;
}
/**
* Check if the bounding box collides with a block (passable + accounting
* for workarounds).
*
* @param access
* @param minX
* @param minY
* @param minZ
* @param maxX
* @param maxY
* @param maxZ
* @return
*/
public static final boolean isPassableBox(final BlockCache access,
final double minX, final double minY, final double minZ,
final double maxX, final double maxY, final double maxZ) {
final int iMinX = Location.locToBlock(minX);
final int iMaxX = Location.locToBlock(maxX);
final int iMinY = Location.locToBlock(minY);
final int iMaxY = Location.locToBlock(maxY);
final int iMinZ = Location.locToBlock(minZ);
final int iMaxZ = Location.locToBlock(maxZ);
for (int x = iMinX; x <= iMaxX; x++) {
for (int z = iMinZ; z <= iMaxZ; z++) {
for (int y = iMinY; y <= iMaxY; y++) {
if (!isPassableBox(access, x, y, z, minX, minY, minZ, maxX, maxY, maxZ)) {
return false;
}
}
}
}
return true;
}
/**
* Add the block coordinates that are colliding via a isPassableBox check
* for the given bounds to the given container.

View File

@ -86,6 +86,9 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
/** Simple test if the exact position is passable. */
Boolean passable = null;
/** Bounding box collides with blocks. */
Boolean passableBox = null;
/** Is the player above stairs? */
Boolean aboveStairs = null;
@ -756,12 +759,44 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
*/
public boolean isPassable() {
if (passable == null) {
passable = BlockProperties.isPassable(blockCache, x, y, z, getTypeId());
// passable = BlockProperties.isPassableExact(blockCache, x, y, z, getTypeId());
if (isBlockFlagsPassable()) {
passable = true;
}
else {
passable = BlockProperties.isPassable(blockCache, x, y, z, getTypeId());
//passable = BlockProperties.isPassableExact(blockCache, x, y, z, getTypeId());
}
}
return passable;
}
/**
* Test if the bounding box is colliding (passable check with accounting for
* workarounds).
*
* @return
*/
public boolean isPassableBox() {
// TODO: Might need a variation with margins as parameters.
if (passableBox == null) {
if (isBlockFlagsPassable()) {
passableBox = true;
}
else if (passable != null && !passable) {
passableBox = false;
}
else {
// Fetch.
passableBox = BlockProperties.isPassableBox(blockCache, minX, minY, minZ, maxX, maxY, maxZ);
}
}
return passableBox;
}
private boolean isBlockFlagsPassable() {
return blockFlags != null && (blockFlags & (BlockProperties.F_SOLID | BlockProperties.F_GROUND)) == 0;
}
/**
* Set block flags using yOnGround, unless already set. Check the maximally
* used bounds for the block checking, to have flags ready for faster
@ -907,6 +942,7 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
this.notOnGroundMaxY = other.notOnGroundMaxY;
this.onGroundMinY = other.onGroundMinY;
this.passable = other.passable;
this.passableBox = other.passableBox;
// Access methods.
this.typeId = other.getTypeId();
this.typeIdBelow = other.getTypeIdBelow();
@ -957,7 +993,7 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
// Reset cached values.
typeId = typeIdBelow = data = null;
aboveStairs = inLava = inWater = inWeb = onIce = onGround = onClimbable = passable = null;
aboveStairs = inLava = inWater = inWeb = onIce = onGround = onClimbable = passable = passableBox = null;
onGroundMinY = Double.MAX_VALUE;
notOnGroundMaxY = Double.MIN_VALUE;
blockFlags = null;

View File

@ -180,6 +180,13 @@ public abstract class AxisTracing implements ICollide, ISetMargins {
}
}
private boolean shouldCheckForIgnoredBlocks() {
return ignoreInitiallyColliding && !ignoredBlocks.isEmpty();
}
private boolean isBlockIgnored(final int x, final int y, final int z) {
return ignoredBlocks.containsBlockPosition(x, y, z);
}
private void runAxisY(final double xIn, final double yIn, final double zIn) {
// Skip if there is nothing to iterate.
@ -218,10 +225,10 @@ public abstract class AxisTracing implements ICollide, ISetMargins {
if (step > maxSteps) {
return;
}
final boolean checkInitiallyColliding = ignoreInitiallyColliding && !ignoredBlocks.isEmpty();
final boolean checkInitiallyColliding = shouldCheckForIgnoredBlocks();
for (int x = iMinX; x <= iMaxX; x++) {
for (int z = iMinZ; z <= iMaxZ; z++) {
if (checkInitiallyColliding && ignoredBlocks.containsBlockPosition(x, y, z)) {
if (checkInitiallyColliding && isBlockIgnored(x, y, z)) {
// Ignore.
}
else if (!step(x, y, z, xMin, increment == 1 ? yStart : yEnd, zMin, xMax, increment == 1 ? yEnd : yStart, zMax, Axis.Y_AXIS, increment)) {
@ -272,10 +279,10 @@ public abstract class AxisTracing implements ICollide, ISetMargins {
if (step > maxSteps) {
return;
}
final boolean checkInitiallyColliding = ignoreInitiallyColliding && !ignoredBlocks.isEmpty();
final boolean checkInitiallyColliding = shouldCheckForIgnoredBlocks();
for (int y = iMinY; y <= iMaxY; y++) {
for (int z = iMinZ; z <= iMaxZ; z++) {
if (checkInitiallyColliding && ignoredBlocks.containsBlockPosition(x, y, z)) {
if (checkInitiallyColliding && isBlockIgnored(x, y, z)) {
// Ignore.
}
else if (!step(x, y, z, increment == 1 ? xStart : xEnd, yMin, zMin, increment == 1 ? xEnd : xStart, yMax, zMax, Axis.X_AXIS, increment)) {
@ -320,7 +327,7 @@ public abstract class AxisTracing implements ICollide, ISetMargins {
final int iMaxX = Location.locToBlock(xMax);
final int iStartZ = Location.locToBlock(zStart);
axisStep = 0;
final boolean checkInitiallyColliding = ignoreInitiallyColliding && !ignoredBlocks.isEmpty();
final boolean checkInitiallyColliding = shouldCheckForIgnoredBlocks();
for (int z = iStartZ; z != iEndZ; z += increment) {
++step;
++axisStep;
@ -329,7 +336,7 @@ public abstract class AxisTracing implements ICollide, ISetMargins {
}
for (int y = iMinY; y <= iMaxY; y++) {
for (int x = iMinX; x <= iMaxX; x++) {
if (checkInitiallyColliding && ignoredBlocks.containsBlockPosition(x, y, z)) {
if (checkInitiallyColliding && isBlockIgnored(x, y, z)) {
// Ignore.
}
else if (!step(x, y, z, xMin, yMin, increment == 1 ? zStart : zEnd, xMax, yMax, increment == 1 ? zEnd : zStart, Axis.Z_AXIS, increment)) {