fixed up raytracer and placement option

This commit is contained in:
Leijurv
2021-05-10 00:23:10 -07:00
parent fdf899eabf
commit 5c85d0cfd1
8 changed files with 597 additions and 69 deletions

View File

@@ -245,10 +245,10 @@ public final class BetterBlockPos extends BlockPos {
@Nonnull
public String toString() {
return String.format(
"BetterBlockPos{x=%s,y=%s,z=%s}",
SettingsUtil.maybeCensor(x),
SettingsUtil.maybeCensor(y),
SettingsUtil.maybeCensor(z)
"BetterBlockPos{x=%d,y=%d,z=%d}",
x,
y,
z
);
}
}

View File

@@ -28,10 +28,11 @@ import java.util.*;
public final class BlockStateCachedData {
private static final BlockStateCachedData[] PER_STATE = Main.DATA_PROVIDER.all();
public static final BlockStateCachedData SCAFFOLDING = new BlockStateCachedData(false, true, true);
public static final BlockStateCachedData SCAFFOLDING = new BlockStateCachedData(false, true, true, Half.EITHER, false);
public final boolean canWalkOn;
public final boolean isAir;
public final boolean mustSneakWhenPlacingAgainstMe;
private final boolean[] presentsTopHalfFaceForPlacement;
private final boolean[] presentsBottomHalfFaceForPlacement;
private final List<BlockStatePlacementOption> options;
@@ -40,17 +41,17 @@ public final class BlockStateCachedData {
return PER_STATE[state];
}
public BlockStateCachedData(boolean isAir, boolean canPlaceAgainstAtAll, boolean canWalkOn) {
this(isAir, canPlaceAgainstAtAll, canWalkOn, Half.EITHER);
}
public BlockStateCachedData(boolean isAir, boolean canPlaceAgainstAtAll, boolean canWalkOn, Half half) {
public BlockStateCachedData(boolean isAir, boolean canPlaceAgainstAtAll, boolean canWalkOn, Half half, boolean mustSneakWhenPlacingAgainstMe) {
this.isAir = isAir;
this.canWalkOn = canWalkOn;
this.mustSneakWhenPlacingAgainstMe = mustSneakWhenPlacingAgainstMe;
this.options = Collections.unmodifiableList(calcOptions(canPlaceAgainstAtAll, half));
this.presentsTopHalfFaceForPlacement = new boolean[Face.VALUES.length];
this.presentsBottomHalfFaceForPlacement = new boolean[Face.VALUES.length];
setupFacesPresented(canPlaceAgainstAtAll, half);
if (mustSneakWhenPlacingAgainstMe && half != Half.EITHER) {
throw new IllegalArgumentException();
}
}
private void setupFacesPresented(boolean canPlaceAgainstAtAll, Half half) {
@@ -86,22 +87,23 @@ public final class BlockStateCachedData {
}
@Nullable
private Half presentsFace(Face face, Half half) {
private PlaceAgainstData presentsFace(Face face, Half half) {
if ((face == Face.UP || face == Face.DOWN) && half != Half.EITHER) {
throw new IllegalStateException();
}
boolean top = presentsTopHalfFaceForPlacement[face.index] && (half == Half.EITHER || half == Half.TOP);
boolean bottom = presentsBottomHalfFaceForPlacement[face.index] && (half == Half.EITHER || half == Half.BOTTOM);
Half intersectedHalf; // the half that both we present, and they accept. not necessarily equal to either. slab-against-block and block-against-slab will both have this as top/bottom, not either.
if (top && bottom) {
return Half.EITHER;
intersectedHalf = Half.EITHER;
} else if (top) {
intersectedHalf = Half.TOP;
} else if (bottom) {
intersectedHalf = Half.BOTTOM;
} else {
return null;
}
if (top) {
return Half.TOP;
}
if (bottom) {
return Half.BOTTOM;
}
return null;
return PlaceAgainstData.get(intersectedHalf, mustSneakWhenPlacingAgainstMe);
}
private List<BlockStatePlacementOption> calcOptions(boolean canPlaceAgainstAtAll, Half half) {
@@ -135,9 +137,9 @@ public final class BlockStateCachedData {
@Nullable
public Half canBeDoneAgainstMe(BlockStatePlacementOption placement) {
public PlaceAgainstData canBeDoneAgainstMe(BlockStatePlacementOption placement) {
if (Main.fakePlacementForPerformanceTesting) {
return Main.RAND.nextInt(10) < 8 ? Half.EITHER : null;
return Main.RAND.nextInt(10) < 8 ? PlaceAgainstData.EITHER : null;
}
Face myFace = placement.against.opposite();

View File

@@ -17,7 +17,10 @@
package baritone.builder;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -47,7 +50,14 @@ public class BlockStatePlacementOption {
validate(against, half, playerMustBeFacing);
}
public Optional<Raytracer.Raytrace> computeTraceOptions(Half againstHalf, int playerSupportingX, double playerEyeY, int playerSupportingZ, PlayerVantage vantage, double blockReachDistance) {
/**
* This value must be greater than the face projections.
* <p>
* Otherwise certain stair placements would not work. This is verified in this class's sanityCheck.
*/
private static final double LOOSE_CENTER_DISTANCE = 0.15;
public List<Raytracer.Raytrace> computeTraceOptions(Half againstHalf, int playerSupportingX, double playerEyeY, int playerSupportingZ, PlayerVantage vantage, double blockReachDistance) {
if (againstHalf != half && half != Half.EITHER) { // narrowing is ok (EITHER -> TOP/BOTTOM) but widening isn't (TOP/BOTTOM -> EITHER)
throw new IllegalStateException();
}
@@ -55,10 +65,10 @@ public class BlockStatePlacementOption {
Vec2d center = Vec2d.HALVED_CENTER.plus(playerSupportingX, playerSupportingZ);
switch (vantage) {
case LOOSE_CENTER: {
acceptableVantages.add(center.plus(0.15, 0));
acceptableVantages.add(center.plus(-0.15, 0));
acceptableVantages.add(center.plus(0, 0.15));
acceptableVantages.add(center.plus(0, -0.15));
acceptableVantages.add(center.plus(LOOSE_CENTER_DISTANCE, 0));
acceptableVantages.add(center.plus(-LOOSE_CENTER_DISTANCE, 0));
acceptableVantages.add(center.plus(0, LOOSE_CENTER_DISTANCE));
acceptableVantages.add(center.plus(0, -LOOSE_CENTER_DISTANCE));
// no break!
} // FALLTHROUGH!
case STRICT_CENTER: {
@@ -79,35 +89,59 @@ public class BlockStatePlacementOption {
// direction from placed block to place-against block = this.against
long blockPlacedAt = 0;
long placeAgainstPos = against.offset(blockPlacedAt);
double sq = blockReachDistance * blockReachDistance;
return acceptableVantages
return sanityCheckTraces(acceptableVantages
.stream()
.map(playerEyeXZ -> new Vec3d(playerEyeXZ.x, playerEyeY, playerEyeXZ.z))
.flatMap(eye ->
Stream.of(FACE_PROJECTION_CACHE[against.index])
.filter(hit -> eye.distSq(hit) < sq)
.filter(this::hitOk)
.filter(hit -> eye.distSq(hit) < blockReachDistance * blockReachDistance)
.filter(hit -> directionOk(eye, hit))
.<Supplier<Optional<Raytracer.Raytrace>>>map(hit -> () -> Raytracer.runTrace(eye, placeAgainstPos, against.opposite(), hit))
)
.collect(Collectors.toList())
.parallelStream() // wrap it like this because flatMap forces .sequential() on the interior child stream, defeating the point
// TODO switch back to parallelStream
.stream() // wrap it like this because flatMap forces .sequential() on the interior child stream, defeating the point
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.min(Comparator.naturalOrder());
.sorted()
.collect(Collectors.toList()));
}
private boolean hitOk(Vec3d hit) {
if (half == Half.EITHER) {
return true;
} else if (hit.y == 0.1) {
return half == Half.BOTTOM;
} else if (hit.y == 0.5) {
return false;
} else if (hit.y == 0.9) {
return half == Half.TOP;
} else {
throw new IllegalStateException();
}
}
private boolean directionOk(Vec3d eye, Vec3d hit) {
if (playerMustBeFacing.isPresent()) {
return eye.flatDirectionTo(hit) == playerMustBeFacing.get();
}
// TODO include the other conditions for stupid blocks like pistons
return true;
}
private static final Vec3d[][] FACE_PROJECTION_CACHE;
static {
Vec2d center = new Vec2d(0.5, 0.5);
double[] diffs = {-0.4, 0, +0.4};
double[] diffs = {0.1, 0.5, 0.9};
FACE_PROJECTION_CACHE = new Vec3d[Face.NUM_FACES][diffs.length * diffs.length];
for (Face face : Face.VALUES) {
int i = 0;
for (double dx : diffs) {
for (double dz : diffs) {
FACE_PROJECTION_CACHE[face.index][i++] = new Vec3d(project(center.plus(dx, dz).toArr(), face));
FACE_PROJECTION_CACHE[face.index][i++] = new Vec3d(project(new double[]{dx, dz}, face));
}
}
}
@@ -173,4 +207,58 @@ public class BlockStatePlacementOption {
}
});
}
static {
if (Main.DEBUG) {
sanityCheck();
}
}
public static void sanityCheck() {
// standing at 1,0,0
// block to be placed at 0,0,0
// placing against 0,0,-1
// eye is at 1, 1.62, 0
// north or west
StringBuilder sanity = new StringBuilder();
for (PlayerVantage vantage : new PlayerVantage[]{PlayerVantage.STRICT_CENTER, PlayerVantage.LOOSE_CENTER}) {
for (Face playerFacing : new Face[]{Face.NORTH, Face.EAST, Face.WEST}) {
sanity.append(vantage).append(playerFacing);
List<Raytracer.Raytrace> traces = BlockStatePlacementOption.get(Face.NORTH, Half.BOTTOM, Optional.of(playerFacing)).computeTraceOptions(Half.BOTTOM, 1, 1.62, 0, vantage, 4);
sanity.append(traces.size());
sanity.append(" ");
if (!traces.isEmpty()) {
for (double d : new double[]{traces.get(0).playerEye.x, traces.get(0).playerEye.z}) {
double base = d > 1 ? 1.5 : 0.5;
boolean a = d == base - LOOSE_CENTER_DISTANCE;
boolean b = d == base;
boolean c = d == base + LOOSE_CENTER_DISTANCE;
if (!a && !b && !c) {
throw new IllegalStateException("Wrong " + d);
}
sanity.append(a).append(" ").append(b).append(" ").append(c).append(" ");
}
}
sanity.append(traces.stream().mapToDouble(Raytracer.Raytrace::centerDistApprox).distinct().count());
sanity.append(";");
}
}
String res = sanity.toString();
String should = "STRICT_CENTERNORTH0 0;STRICT_CENTEREAST0 0;STRICT_CENTERWEST3 false true false false true false 1;LOOSE_CENTERNORTH2 true false false false true false 1;LOOSE_CENTEREAST0 0;LOOSE_CENTERWEST13 false true false false true false 2;";
if (!res.equals(should)) {
System.out.println(res);
System.out.println(should);
throw new IllegalStateException(res);
}
}
private static List<Raytracer.Raytrace> sanityCheckTraces(List<Raytracer.Raytrace> traces) {
if (Main.DEBUG && traces.stream().mapToDouble(Raytracer.Raytrace::centerDistApprox).distinct().count() > 2) {
throw new IllegalStateException();
}
return traces;
}
}

View File

@@ -19,6 +19,8 @@ package baritone.builder;
import baritone.api.utils.BetterBlockPos;
import baritone.builder.mc.VanillaBlockStateDataProvider;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
@@ -85,7 +87,7 @@ public class Main {
{
System.out.println(BetterBlockPos.fromLong(BetterBlockPos.toLong(150, 150, 150)));
}
for (int i = 0; i < 10; i++) {
for (int i = 0; i < 0; i++) {
long aaa = System.currentTimeMillis();
int[][][] test = new int[64][64][64];
int based = Block.BLOCK_STATE_IDS.get(Blocks.DIRT.getDefaultState());
@@ -141,14 +143,7 @@ public class Main {
Thread.sleep(500);
//scaffolding.enable(0);
}
{
// stadning at 1,0,0
// block to be placed at 0,0,0
// placing against 0,0,-1
// eye is at 1, 1.62, 0
}
for (int i = 0; i < 10; i++) {
for (int i = 0; i < 0; i++) {
Stream.of(new Object())
.flatMap(ignored -> IntStream.range(0, 100).boxed())
.parallel()
@@ -161,6 +156,89 @@ public class Main {
.collect(Collectors.toList()).parallelStream()
.forEach(x -> System.out.println(x + ""));
}
{
BlockStatePlacementOption.sanityCheck();
}
{
/*Raytracer.raytraceMode++;
Raytracer.raytraceMode %= 3;*/
Random rand = new Random(5021);
DoubleArrayList A = new DoubleArrayList();
DoubleArrayList B = new DoubleArrayList();
DoubleArrayList C = new DoubleArrayList();
DoubleArrayList D = new DoubleArrayList();
DoubleArrayList E = new DoubleArrayList();
DoubleArrayList F = new DoubleArrayList();
LongArrayList G = new LongArrayList();
long a = System.currentTimeMillis();
for (int trial = 0; trial < 10_000_000; ) {
Vec3d playerEye = new Vec3d(rand.nextDouble() * 5 - 2.5, rand.nextDouble() * 5, rand.nextDouble() * 5 - 2.5);
long eyeBlock = playerEye.getRoundedToZeroPositionUnsafeDontUse();
if (eyeBlock == 0) {
// origin, unlucky
continue;
}
Face placeToAgainst = Face.VALUES[rand.nextInt(Face.NUM_FACES)];
Face againstToPlace = placeToAgainst.opposite();
long placeAgainst = placeToAgainst.offset(0);
if (eyeBlock == placeAgainst) {
continue;
}
double[] hitVec = new double[3];
for (int i = 0; i < 3; i++) {
switch (placeToAgainst.vec[i]) {
case -1: {
hitVec[i] = 0;
break;
}
case 0: {
hitVec[i] = rand.nextDouble();
break;
}
case 1: {
hitVec[i] = 1;
break;
}
}
}
Vec3d hit = new Vec3d(hitVec);
Raytracer.runTrace(playerEye, placeAgainst, againstToPlace, hit);
A.add(playerEye.x);
B.add(playerEye.y);
C.add(playerEye.z);
D.add(hit.x);
E.add(hit.y);
F.add(hit.z);
G.add(placeAgainst);
trial++;
}
long b = System.currentTimeMillis();
System.out.println("Nominal first run with overhead: " + (b - a) + "ms");
for (int it = 0; it < 20; it++) {
{
Thread.sleep(1000);
System.gc();
Thread.sleep(1000);
long start = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
Raytracer.rayTraceZoomy(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i));
}
long end = System.currentTimeMillis();
System.out.println("Branchless took " + (end - start) + "ms");
}
{
Thread.sleep(1000);
System.gc();
Thread.sleep(1000);
long start = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
Raytracer.rayTraceZoomyBranchy(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i));
}
long end = System.currentTimeMillis();
System.out.println("Branchy took " + (end - start) + "ms");
}
}
}
System.exit(0);
}
}

View File

@@ -0,0 +1,49 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone.builder;
public enum PlaceAgainstData {
TOP(Half.TOP, false),
BOTTOM(Half.BOTTOM, false),
EITHER(Half.EITHER, false),
SNEAK_TOP(Half.TOP, true), // only if you need to, like, place a top slab against a crafting table
SNEAK_BOTTOM(Half.BOTTOM, true), // only if you need to, like, place a top slab against a crafting table
SNEAK_EITHER(Half.EITHER, true);
public final Half half; // like if its a slab
public final boolean mustSneak; // like if its a crafting table
PlaceAgainstData(Half half, boolean mustSneak) {
this.half = half;
this.mustSneak = mustSneak;
}
public static PlaceAgainstData get(Half half, boolean mustSneak) {
// oh, the things i do, out of fear of the garbage collector
switch (half) {
case TOP:
return mustSneak ? SNEAK_TOP : TOP;
case BOTTOM:
return mustSneak ? SNEAK_BOTTOM : BOTTOM;
case EITHER:
return mustSneak ? SNEAK_EITHER : EITHER;
default:
throw new IllegalArgumentException();
}
}
}

View File

@@ -22,6 +22,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Traces rays
@@ -29,17 +30,25 @@ import java.util.Optional;
public class Raytracer {
public static Optional<Raytrace> runTrace(Vec3d playerEye, long againstPos, Face againstFace, Vec3d hit) {
// TODO this totally could be cached tho...
if (againstFace.offset(againstPos) != 0) {
throw new IllegalStateException("sanity check - for now we assume that all placed blocks end up at 0,0,0");
}
if (toLong(playerEye) == 0) {
throw new IllegalStateException("player eye is within the block we want to place, this is maybe possible? idk i suppose you could do this with, like, a torch? still seems weird. idk if this should be allowed");
}
if (toLong(playerEye) == againstPos) {
throw new IllegalStateException("player eye is within the block we want to place against, this is DEFINITELY impossible");
}
//System.out.println(BetterBlockPos.fromLong(toLong(playerEye)) + " to " + BetterBlockPos.fromLong(toLong(hit)) + " aka " + playerEye + " to " + hit);
if (Main.STRICT_Y && floor(playerEye.y) < 0) {
throw new IllegalStateException("im lazy and dont want to fix occupancyCountByY");
}
long hitPos = BetterBlockPos.toLong(floor(hit.x), floor(hit.y), floor(hit.z));
long hitPos = toLong(hit);
if (hitPos != againstPos && hitPos != 0) {
throw new IllegalStateException("ambiguous or incorrect hitvec?");
}
LongArrayList trace = rayTrace(playerEye.x, playerEye.y, playerEye.z, hit.x, hit.y, hit.z);
LongArrayList trace = rayTrace(playerEye.x, playerEye.y, playerEye.z, hit.x, hit.y, hit.z, againstPos);
if (trace.size() < 2) {
throw new IllegalStateException();
}
@@ -52,6 +61,10 @@ public class Raytracer {
return Optional.of(new Raytrace(playerEye, againstPos, againstFace, hit, trace));
}
private static long toLong(Vec3d hit) {
return BetterBlockPos.toLong(floor(hit.x), floor(hit.y), floor(hit.z));
}
public static class Raytrace implements Comparable<Raytrace> {
public final long againstPos;
@@ -69,14 +82,20 @@ public class Raytracer {
this.playerEye = playerEye;
this.hit = hit;
if (trace.getLong(trace.size() - 1) != againstPos) {
print(trace);
throw new IllegalStateException();
}
if (trace.getLong(trace.size() - 2) != 0) {
throw new IllegalStateException();
}
for (int i = 0; i < trace.size() - 1; i++) {
if (!adjacent(trace.getLong(i), trace.getLong(i + 1))) {
throw new IllegalStateException(BetterBlockPos.fromLong(trace.getLong(i)) + " to " + BetterBlockPos.fromLong(trace.getLong(i + 1)));
}
}
trace.removeLong(trace.size() - 1); // againstPos doesn't ACTUALLY need to be air, so remove it. it was only there for sanity checking and confirming which face we collided with first
trace.trim();
if (trace.getLong(0) != BetterBlockPos.toLong(floor(playerEye.x), floor(playerEye.y), floor(playerEye.z))) {
if (trace.getLong(0) != toLong(playerEye)) {
throw new IllegalStateException();
}
this.passedThrough = trace.elements();
@@ -91,13 +110,14 @@ public class Raytracer {
for (int i = passedThrough.length - 1; i >= 0; i--) {
long pos = passedThrough[i];
int y = BetterBlockPos.YfromLong(pos);
if (list.size() == y) {
if (list.size() == y) { // works because we removed the last trace element (against), which could have negative y
list.add(0);
}
if (list.size() != y + 1) { // this works because we go in reverse order
throw new IllegalStateException("nonconsecutive");
}
if (pos == freebieTop) {
// only here for correctness in spirit, technically not needed for comparison since it will exist in all of them
continue;
}
if (pos == freebieBottom) {
@@ -137,24 +157,103 @@ public class Raytracer {
}
}
{ // if occupancy counts match, tiebreak with strict center winning over loose center
int cmp = Double.compare(centerDist(), o.centerDist());
int cmp = Double.compare(centerDistApprox(), o.centerDistApprox());
if (cmp != 0) {
return cmp;
}
}
{ // if center status matches, finally tiebreak with simple ray length
return Double.compare(hit.distSq(playerEye), o.hit.distSq(o.playerEye));
return Double.compare(distSq(), o.distSq());
}
}
private double centerDist() {
public double centerDistApprox() {
// calculate distance to center of block but intentionally round it off
// the intent is for LOOSE_CENTER to always tie with itself, even though floating point inaccuracy would make it unequal if we did a direct Double.compare
double dx = playerEye.x - (floor(playerEye.x) + 0.5d);
double dz = playerEye.z - (floor(playerEye.z) + 0.5d);
return dx * dx + dz * dz;
double dist = dx * dx + dz * dz;
dist = Math.round(dist * 1000000);
return dist;
}
public double distSq() {
return hit.distSq(playerEye);
}
}
private static LongArrayList rayTrace(double startX, double startY, double startZ, double endX, double endY, double endZ) {
/**
* If I add 10 to all the numbers I raytrace, then subtract them afterwards, then I can just use (int) instead of the nasty BRANCHING floor.
* <p>
* No difference raytracing from -2 to -1 as it is to raytrace from 8 to 9. Just add ten!
*/
private static final int POSITIVITY_OFFSET = 10;
private static final int NUM_STEPS = 10_000;
public static int raytraceMode = 2;
private static LongArrayList rayTrace(double rawStartX, double rawStartY, double rawStartZ, double endX, double endY, double endZ, long againstPos) {
LongArrayList slow = raytraceMode == 0 || Main.DEBUG ? rayTraceSlow(rawStartX, rawStartY, rawStartZ, endX, endY, endZ) : null;
LongArrayList fast = raytraceMode == 1 || Main.DEBUG ? rayTraceFast(rawStartX, rawStartY, rawStartZ, endX, endY, endZ) : null;
LongArrayList faster = raytraceMode == 2 || Main.DEBUG ? rayTraceZoomy(rawStartX, rawStartY, rawStartZ, endX, endY, endZ, againstPos) : null;
if (Main.DEBUG) {
if (fast.equals(slow) && fast.equals(faster)) {
} else {
System.out.println(rawStartX + " " + rawStartY + " " + rawStartZ + " " + endX + " " + endY + " " + endZ + " " + againstPos);
print(slow);
print(fast);
print(faster);
throw new IllegalStateException();
}
}
return fast == null ? slow == null ? faster : slow : fast;
}
private static void print(LongArrayList trace) {
System.out.println(trace.stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
}
private static LongArrayList rayTraceFast(double rawStartX, double rawStartY, double rawStartZ, double endX, double endY, double endZ) {
if (willFlipSign(rawStartX) || willFlipSign(rawStartY) || willFlipSign(rawStartZ) || willFlipSign(endX) || willFlipSign(endY) || willFlipSign(endZ)) {
throw new IllegalStateException("I suppose this could happen if you set the block reach distance absurdly high? Don't do that.");
}
double diffX = endX - rawStartX;
double diffY = endY - rawStartY;
double diffZ = endZ - rawStartZ;
if (Math.abs(diffX) < 0.1 && Math.abs(diffY) < 0.1 && Math.abs(diffZ) < 0.1) {
// need more checks than before because now the tightest inner do-while does NOT check step against any upper limit at all
// therefore, if diff was zero, it would truly get stuck indefinitely, unlike previously where it would bail out at 10010
throw new IllegalArgumentException("throwing exception instead of entering infinite inner loop");
}
double startX = rawStartX + POSITIVITY_OFFSET;
double startY = rawStartY + POSITIVITY_OFFSET;
double startZ = rawStartZ + POSITIVITY_OFFSET;
int x = Integer.MIN_VALUE;
int y = Integer.MIN_VALUE;
int z = Integer.MIN_VALUE;
LongArrayList voxelsIntersected = new LongArrayList();
int step = 0;
double mult = 1.0d / NUM_STEPS;
double frac;
while (step < NUM_STEPS) {
do frac = ++step * mult;
while (((x ^ (x = (int) (startX + diffX * frac))) | (y ^ (y = (int) (startY + diffY * frac))) | (z ^ (z = (int) (startZ + diffZ * frac)))) == 0);
voxelsIntersected.add(BetterBlockPos.toLong(x - POSITIVITY_OFFSET, y - POSITIVITY_OFFSET, z - POSITIVITY_OFFSET));
}
if (step > NUM_STEPS + 1) {
throw new IllegalStateException("No floating point inaccuracies allowed. Or, at least, no more than 2 parts in 10,000 of wiggle room lol. " + step);
}
return voxelsIntersected;
}
/**
* Here's an alternate implementation of the above that is functionally the same, just simpler (and slower)
* <p>
* The inner loop branches seven times instead of one, for example.
*/
private static LongArrayList rayTraceSlow(double startX, double startY, double startZ, double endX, double endY, double endZ) {
// i'd love to use strictfp here and on floor, but it could unironically prevent a much needed JIT to native, so I won't, sadly
double diffX = endX - startX;
double diffY = endY - startY;
@@ -164,29 +263,31 @@ public class Raytracer {
int prevY = Integer.MIN_VALUE;
int prevZ = Integer.MIN_VALUE;
LongArrayList ret = new LongArrayList();
for (int step = 0; step <= 10010; step++) { // not a typo 😈😈😈😈😈😈😈😈😈😈😈
double frac = step / 10000.0d; // go THROUGH the face by a little bit, poke through into the block
int x = floor(startX + diffX * frac);
int y = floor(startY + diffY * frac);
int z = floor(startZ + diffZ * frac);
if (x == prevX && y == prevY && z == prevZ) {
int ourLimit = NUM_STEPS + 1;
for (int step = 0; step <= ourLimit; step++) { // 1 branch (step <= ourLimit)
double frac = step / (double) NUM_STEPS; // go THROUGH the face by a little bit, poke through into the block
int x = floor(startX + diffX * frac); // 1 branch in floor
int y = floor(startY + diffY * frac); // 1 branch in floor
int z = floor(startZ + diffZ * frac); // 1 branch in floor
if (x == prevX && y == prevY && z == prevZ) { // 3 branches (due to && short circuiting)
continue;
}
prevX = x;
prevY = y;
prevZ = z;
long toAdd = BetterBlockPos.toLong(x, y, z);
if (!ret.isEmpty()) {
long prev = ret.getLong(ret.size() - 1);
if (!adjacent(toAdd, prev)) {
throw new IllegalStateException(BetterBlockPos.fromLong(prev) + " to " + BetterBlockPos.fromLong(toAdd));
}
}
ret.add(toAdd);
ret.add(BetterBlockPos.toLong(x, y, z));
}
return ret;
}
private static boolean willFlipSign(double pos) {
return flippedSign(pos + POSITIVITY_OFFSET);
}
private static boolean flippedSign(double pos) {
return pos < 1d;
}
private static boolean adjacent(long a, long b) {
if (a == b) {
throw new IllegalStateException();
@@ -209,4 +310,170 @@ public class Raytracer {
return rawCast;
}
}
public static LongArrayList rayTraceZoomy(double startX, double startY, double startZ, double endX, double endY, double endZ, long againstPos) {
if (endX < 0 || endX > 1 || endY < 0 || endY > 1 || endZ < 0 || endZ > 1) {
throw new IllegalStateException("won't work");
}
if (flippedSign(startX += POSITIVITY_OFFSET) | flippedSign(startY += POSITIVITY_OFFSET) | flippedSign(startZ += POSITIVITY_OFFSET) | flippedSign(endX += POSITIVITY_OFFSET) | flippedSign(endY += POSITIVITY_OFFSET) | flippedSign(endZ += POSITIVITY_OFFSET)) {
throw new IllegalStateException("I suppose this could happen if you set the block reach distance absurdly high? Don't do that.");
}
int voxelEndX = BetterBlockPos.XfromLong(againstPos) + POSITIVITY_OFFSET;
int voxelEndY = BetterBlockPos.YfromLong(againstPos) + POSITIVITY_OFFSET;
int voxelEndZ = BetterBlockPos.ZfromLong(againstPos) + POSITIVITY_OFFSET;
int voxelInX = (int) startX;
int voxelInY = (int) startY;
int voxelInZ = (int) startZ;
if (startX == (double) voxelInX || startY == (double) voxelInY || startZ == (double) voxelInZ) {
throw new IllegalStateException("Integral starting coordinates not supported ever since I removed the -0.0d check");
}
LongArrayList voxelsIntersected = new LongArrayList();
int steps = 64; // default is 200
while (steps-- >= 0) {
long posAsLong = BetterBlockPos.toLong(voxelInX - POSITIVITY_OFFSET, voxelInY - POSITIVITY_OFFSET, voxelInZ - POSITIVITY_OFFSET);
voxelsIntersected.add(posAsLong);
if (posAsLong == againstPos) {
if (voxelsIntersected.size() == 1 || voxelsIntersected.getLong(voxelsIntersected.size() - 2) != 0) {
voxelsIntersected.add(0);
}
return voxelsIntersected;
}
double nextIntegerX, nextIntegerY, nextIntegerZ;
// potentially more based branchless impl?
nextIntegerX = voxelInX + ((voxelInX - voxelEndX) >>> 31); // if voxelEnd > voxelIn, then voxelIn-voxelEnd will be negative, meaning the sign bit is 1
nextIntegerY = voxelInY + ((voxelInY - voxelEndY) >>> 31); // if we do an unsigned right shift by 31, that sign bit becomes the LSB
nextIntegerZ = voxelInZ + ((voxelInZ - voxelEndZ) >>> 31); // therefore, this increments nextInteger iff EndX>inX, otherwise it leaves it alone
// remember: don't have to worry about the case when voxelEnd == voxelIn, because nextInteger value wont be used
double fracIfSkipX = 100;
double fracIfSkipY = 100;
double fracIfSkipZ = 100;
double distanceFromStartToEndX = endX - startX;
double distanceFromStartToEndY = endY - startY;
double distanceFromStartToEndZ = endZ - startZ;
if (voxelEndX != voxelInX) {
fracIfSkipX = (nextIntegerX - startX) / distanceFromStartToEndX;
}
if (voxelEndY != voxelInY) {
fracIfSkipY = (nextIntegerY - startY) / distanceFromStartToEndY;
}
if (voxelEndZ != voxelInZ) {
fracIfSkipZ = (nextIntegerZ - startZ) / distanceFromStartToEndZ;
}
int dx = 0;
int dy = 0;
int dz = 0;
if (fracIfSkipX < fracIfSkipY && fracIfSkipX < fracIfSkipZ) {
// note: voxelEndX == voxelInX is impossible because allowSkip would be set to false in that case, meaning that the elapsed distance would stay at default
dx = (voxelEndX - voxelInX) >>> 31; // TODO should i set this "dx" way up top at the same time as i set nextIntegerX?
startX = nextIntegerX;
startY += distanceFromStartToEndY * fracIfSkipX;
startZ += distanceFromStartToEndZ * fracIfSkipX;
} else if (fracIfSkipY < fracIfSkipZ) {
dy = (voxelEndY - voxelInY) >>> 31; // we want dy=1 when endY < inY
startX += distanceFromStartToEndX * fracIfSkipY;
startY = nextIntegerY;
startZ += distanceFromStartToEndZ * fracIfSkipY;
} else {
dz = (voxelEndZ - voxelInZ) >>> 31;
startX += distanceFromStartToEndX * fracIfSkipZ;
startY += distanceFromStartToEndY * fracIfSkipZ;
startZ = nextIntegerZ;
}
voxelInX = ((int) startX) - dx; // TODO is it faster to paste this block of 3 lines into each of the 3 if branches? we know 2/3 subtracts will be zero, so it would save two subtracts, but is that worth the longer bytecode?
voxelInY = ((int) startY) - dy;
voxelInZ = ((int) startZ) - dz;
}
print(voxelsIntersected);
throw new IllegalStateException();
}
public static LongArrayList rayTraceZoomyBranchy(double startX, double startY, double startZ, double endX, double endY, double endZ, long againstPos) {
if (endX < 0 || endX > 1 || endY < 0 || endY > 1 || endZ < 0 || endZ > 1) {
throw new IllegalStateException("won't work");
}
if (flippedSign(startX += POSITIVITY_OFFSET) | flippedSign(startY += POSITIVITY_OFFSET) | flippedSign(startZ += POSITIVITY_OFFSET) | flippedSign(endX += POSITIVITY_OFFSET) | flippedSign(endY += POSITIVITY_OFFSET) | flippedSign(endZ += POSITIVITY_OFFSET)) {
throw new IllegalStateException("I suppose this could happen if you set the block reach distance absurdly high? Don't do that.");
}
int voxelEndX = BetterBlockPos.XfromLong(againstPos) + POSITIVITY_OFFSET;
int voxelEndY = BetterBlockPos.YfromLong(againstPos) + POSITIVITY_OFFSET;
int voxelEndZ = BetterBlockPos.ZfromLong(againstPos) + POSITIVITY_OFFSET;
int voxelInX = (int) startX;
int voxelInY = (int) startY;
int voxelInZ = (int) startZ;
if (startX == (double) voxelInX || startY == (double) voxelInY || startZ == (double) voxelInZ) {
throw new IllegalStateException("Integral starting coordinates not supported ever since I removed the -0.0d check");
}
LongArrayList voxelsIntersected = new LongArrayList();
int steps = 64; // default is 200
while (steps-- >= 0) {
long posAsLong = BetterBlockPos.toLong(voxelInX - POSITIVITY_OFFSET, voxelInY - POSITIVITY_OFFSET, voxelInZ - POSITIVITY_OFFSET);
voxelsIntersected.add(posAsLong);
if (posAsLong == againstPos) {
if (voxelsIntersected.size() == 1 || voxelsIntersected.getLong(voxelsIntersected.size() - 2) != 0) {
voxelsIntersected.add(0);
}
return voxelsIntersected;
}
double nextIntegerX, nextIntegerY, nextIntegerZ;
// normal impl
nextIntegerX = voxelEndX > voxelInX ? voxelInX + 1 : voxelInX;
nextIntegerY = voxelEndY > voxelInY ? voxelInY + 1 : voxelInY;
nextIntegerZ = voxelEndZ > voxelInZ ? voxelInZ + 1 : voxelInZ;
double fracIfSkipX = 100;
double fracIfSkipY = 100;
double fracIfSkipZ = 100;
double distanceFromStartToEndX = endX - startX;
double distanceFromStartToEndY = endY - startY;
double distanceFromStartToEndZ = endZ - startZ;
if (voxelEndX != voxelInX) {
fracIfSkipX = (nextIntegerX - startX) / distanceFromStartToEndX;
}
if (voxelEndY != voxelInY) {
fracIfSkipY = (nextIntegerY - startY) / distanceFromStartToEndY;
}
if (voxelEndZ != voxelInZ) {
fracIfSkipZ = (nextIntegerZ - startZ) / distanceFromStartToEndZ;
}
int dx = 0;
int dy = 0;
int dz = 0;
if (fracIfSkipX < fracIfSkipY && fracIfSkipX < fracIfSkipZ) {
// note: voxelEndX == voxelInX is impossible because allowSkip would be set to false in that case, meaning that the elapsed distance would stay at default
if (voxelEndX < voxelInX) {
dx = 1;
}
startX = nextIntegerX;
startY += distanceFromStartToEndY * fracIfSkipX;
startZ += distanceFromStartToEndZ * fracIfSkipX;
} else if (fracIfSkipY < fracIfSkipZ) {
if (voxelEndY < voxelInY) {
dy = 1;
}
startX += distanceFromStartToEndX * fracIfSkipY;
startY = nextIntegerY;
startZ += distanceFromStartToEndZ * fracIfSkipY;
} else {
if (voxelEndZ < voxelInZ) {
dz = 1;
}
startX += distanceFromStartToEndX * fracIfSkipZ;
startY += distanceFromStartToEndY * fracIfSkipZ;
startZ = nextIntegerZ;
}
voxelInX = ((int) startX) - dx; // TODO is it faster to paste this block of 3 lines into each of the 3 if branches? we know 2/3 subtracts will be zero, so it would save two subtracts, but is that worth the longer bytecode?
voxelInY = ((int) startY) - dy;
voxelInZ = ((int) startZ) - dz;
}
print(voxelsIntersected);
throw new IllegalStateException();
}
}

View File

@@ -17,6 +17,8 @@
package baritone.builder;
import baritone.api.utils.BetterBlockPos;
public class Vec3d {
public final double x;
@@ -36,10 +38,51 @@ public class Vec3d {
}
}
public long getRoundedToZeroPositionUnsafeDontUse() {
return BetterBlockPos.toLong((int) x, (int) y, (int) z);
}
public double distSq(Vec3d other) {
double dx = x - other.x;
double dy = y - other.y;
double dz = z - other.z;
return dx * dx + dy * dy + dz * dz;
}
public Face flatDirectionTo(Vec3d dst) {
return new Vec3d(dst.x - x, dst.y - y, dst.z - z).flatDirection();
}
public Face flatDirection() {
if (Math.abs(x) == Math.abs(z)) {
throw new IllegalStateException("ambiguous");
}
if (Math.abs(x) > Math.abs(z)) {
if (x > 0) {
return Face.EAST;
} else {
return Face.WEST;
}
} else {
if (z > 0) {
return Face.SOUTH;
} else {
return Face.NORTH;
}
}
}
@Override
public String toString() {
return "Vec3d{" + x + "," + y + "," + z + "}";
}
static {
for (Face face : Face.HORIZONTALS) {
Face flat = new Vec3d(face.x, face.y, face.z).flatDirection();
if (flat != face) {
throw new IllegalStateException();
}
}
}
}

View File

@@ -18,6 +18,7 @@
package baritone.builder.mc;
import baritone.builder.BlockStateCachedData;
import baritone.builder.Half;
import net.minecraft.block.Block;
import net.minecraft.block.BlockAir;
import net.minecraft.block.state.IBlockState;
@@ -34,9 +35,9 @@ public class BlockStatePropertiesExtractor {
Block block = state.getBlock();
if (block instanceof BlockAir) {
return new BlockStateCachedData(true, false, false);
return new BlockStateCachedData(true, false, false, Half.EITHER, false);
}
boolean normal = block == Blocks.COBBLESTONE || block == Blocks.DIRT;
return new BlockStateCachedData(false, normal, normal);
return new BlockStateCachedData(false, normal, normal, Half.EITHER, false);
}
}