diff --git a/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java b/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java index 32b986912..13d993b43 100644 --- a/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java +++ b/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java @@ -217,7 +217,7 @@ public class BlockStateCachedDataBuilder { if (canPlaceAgainstMe && !collidesWithPlayer) { throw new IllegalStateException(); } - if (playerMustBeFacingInOrderToPlaceMe.vertical) { + if (playerMustBeFacingInOrderToPlaceMe != null && playerMustBeFacingInOrderToPlaceMe.vertical) { throw new IllegalStateException(); } if (Main.STRICT_Y && howCanIBePlaced().stream().anyMatch(opt -> opt.against == Face.UP)) { diff --git a/src/main/java/baritone/builder/CuboidBounds.java b/src/main/java/baritone/builder/CuboidBounds.java index 320745f8c..3ea21b3b5 100644 --- a/src/main/java/baritone/builder/CuboidBounds.java +++ b/src/main/java/baritone/builder/CuboidBounds.java @@ -29,13 +29,21 @@ public class CuboidBounds { public final int sizeX; public final int sizeY; public final int sizeZ; + private final int sizeXMinusOne; + private final int sizeYMinusOne; + private final int sizeZMinusOne; public final int size; + private final int sizeMinusOne; public CuboidBounds(int sizeX, int sizeY, int sizeZ) { this.sizeX = sizeX; this.sizeY = sizeY; this.sizeZ = sizeZ; + this.sizeXMinusOne = sizeX - 1; + this.sizeYMinusOne = sizeY - 1; + this.sizeZMinusOne = sizeZ - 1; this.size = sizeX * sizeY * sizeZ; + this.sizeMinusOne = size - 1; if (Main.DEBUG) { sanityCheck(); } @@ -53,11 +61,19 @@ public class CuboidBounds { } public boolean inRange(int x, int y, int z) { + throw new UnsupportedOperationException("ugh benchmark this tomorrow when im less tired"); + } + + public boolean inRangeBranchy(int x, int y, int z) { return (x >= 0) && (x < sizeX) && (y >= 0) && (y < sizeY) && (z >= 0) && (z < sizeZ); } + public boolean inRangeBranchless(int x, int y, int z) { + return (x | y | z | (sizeXMinusOne - x) | (sizeYMinusOne - y) | (sizeZMinusOne - z)) >= 0; + } + public boolean inRangeIndex(int index) { - return (index >= 0) & (index < size); + return (index | (sizeMinusOne - index)) >= 0; } public boolean inRangePos(long pos) { @@ -124,6 +140,9 @@ public class CuboidBounds { for (int x = 0; x < sizeX; x++) { for (int y = 0; y < sizeY; y++) { for (int z = 0; z < sizeZ; z++) { + if (!inRange(x, y, z)) { + throw new IllegalStateException(); + } if (toIndex(x, y, z) != index) { throw new IllegalStateException(); } @@ -134,5 +153,11 @@ public class CuboidBounds { if (index != size) { throw new IllegalStateException(); } + if (inRange(-1, 0, 0) || inRange(0, -1, 0) || inRange(0, 0, -1)) { + throw new IllegalStateException(); + } + if (inRange(sizeX, 0, 0) || inRange(0, sizeY, 0) || inRange(0, 0, sizeZ)) { + throw new IllegalStateException(); + } } } diff --git a/src/main/java/baritone/builder/IReachabilityProvider.java b/src/main/java/baritone/builder/IReachabilityProvider.java new file mode 100644 index 000000000..3d4449f4a --- /dev/null +++ b/src/main/java/baritone/builder/IReachabilityProvider.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +package baritone.builder; + +import it.unimi.dsi.fastutil.longs.LongList; + +public interface IReachabilityProvider { + + LongList candidates(long playerEyeVoxel); + + static IReachabilityProvider get(DependencyGraphScaffoldingOverlay overlay, PlayerReachSphere sphere) { + try { + return new ReachabilityCache(overlay, sphere); + } catch (ReachabilityCache.SchematicIsTooDenseForThisToMakeSenseException ex) { + return new ReachabilityLive(overlay, sphere); + } + } +} diff --git a/src/main/java/baritone/builder/Main.java b/src/main/java/baritone/builder/Main.java index 4747f83c2..b1e2efba3 100644 --- a/src/main/java/baritone/builder/Main.java +++ b/src/main/java/baritone/builder/Main.java @@ -218,6 +218,25 @@ public class Main { } long b = System.currentTimeMillis(); System.out.println("Nominal first run with overhead: " + (b - a) + "ms"); + boolean checkAgainst = true; + for (int i = 0; i < 10_000_000; i++) { + if (i % 1000 == 0 && checkAgainst) { + System.out.println(i); + } + LongArrayList normal = Raytracer.rayTraceZoomy(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i)); + LongArrayList alternate = Raytracer.rayTraceZoomyAlternate(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i)); + if (!normal.equals(alternate)) { + throw new IllegalStateException(); + } + if (checkAgainst) { + LongArrayList superSlow = Raytracer.rayTraceFast(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i)); + if (!normal.equals(superSlow)) { + Raytracer.print(normal); + Raytracer.print(superSlow); + checkAgainst = false; + } + } + } for (int it = 0; it < 20; it++) { { Thread.sleep(1000); @@ -228,7 +247,7 @@ public class Main { 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"); + System.out.println("Normal took " + (end - start) + "ms"); } { Thread.sleep(1000); @@ -236,10 +255,10 @@ public class Main { 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)); + Raytracer.rayTraceZoomyAlternate(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.out.println("Alternate took " + (end - start) + "ms"); } } } diff --git a/src/main/java/baritone/builder/PlaceOptions.java b/src/main/java/baritone/builder/PlaceOptions.java new file mode 100644 index 000000000..e50e8561b --- /dev/null +++ b/src/main/java/baritone/builder/PlaceOptions.java @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +package baritone.builder; + +public class PlaceOptions { + + IReachabilityProvider provider; + DependencyGraphScaffoldingOverlay overlay; + + public void go(int playerX, int playerFeetBlips, int playerZ) { + + } +} diff --git a/src/main/java/baritone/builder/PlayerReachSphere.java b/src/main/java/baritone/builder/PlayerReachSphere.java new file mode 100644 index 000000000..f7dd78f79 --- /dev/null +++ b/src/main/java/baritone/builder/PlayerReachSphere.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ + +package baritone.builder; + +import baritone.api.utils.BetterBlockPos; +import it.unimi.dsi.fastutil.longs.LongArrayList; + +public class PlayerReachSphere { + + public final long[] positions; + + public PlayerReachSphere(double playerReachDistance) { + int ceiledPlusOne = 1 + (int) Math.ceil(playerReachDistance); + double realSq = playerReachDistance * playerReachDistance; + int ceiledSq = (int) Math.ceil(realSq); + LongArrayList sphere = new LongArrayList(); + for (int x = -ceiledPlusOne; x <= ceiledPlusOne; x++) { + for (int y = -ceiledPlusOne; y <= ceiledPlusOne; y++) { + for (int z = -ceiledPlusOne; z <= ceiledPlusOne; z++) { + int d = closestPossibleDist(x, y, z); + if (d <= ceiledSq && d < realSq) { // int comparison short circuits before floating point + sphere.add(BetterBlockPos.toLong(x, y, z)); + } + } + } + } + sphere.trim(); + positions = sphere.elements(); + } + + public static int closestPossibleDist(int dx, int dy, int dz) { // player eye is within the origin voxel (0,0,0) + dx = lower(dx); + dy = lower(dy); + dz = lower(dz); + return dx * dx + dy * dy + dz * dz; + } + + /** + * Bring closer to 0 by one if not already zero + */ + private static int lower(int v) { + if (v > 0) { + return v - 1; + } else if (v < 0) { + return v + 1; + } else { + return v; + } + } +} diff --git a/src/main/java/baritone/builder/README.md b/src/main/java/baritone/builder/README.md index 61038a740..1283b9f04 100644 --- a/src/main/java/baritone/builder/README.md +++ b/src/main/java/baritone/builder/README.md @@ -23,4 +23,6 @@ I don't want to go too much further into how a traditional graph search algorith I'm not sure how to think about performance for this. Obviously there are copes like "pregenerate a plan for a schematic, reuse it". There are also copes like "just throw it in another thread, who cares how long it takes". Because if I dump in a 128x128x128 schematic of all stone (two million blocks), it takes a few seconds to separate the strongly connected components. Is that okay? I think it is. Lol. -One particularly annoying thing is needing to reimplement portions of Minecraft. For example, there is a bit of code that I can easily call that says "If I looked in this direction right now, and right clicked, what block precisely would it place". Because, well, Minecraft needs to decide what block appears if and when I right click. This works great for current Baritone and I can just call it without needing to worry. For this builder though, I need to do hypotheticals. "If I were standing here on top of this block (that doesn't exist yet), looking at this face of this block (which doesn't exist yet), and right clicked while holding this item (which I don't have), what block would appear?". Minecraft code isn't built like that. There are so many conditions based on Y hit, face hit, player orientation, etc. Just look at the trapdoor place logic to instantly die. So I need to reimplement all the code for "I desire this block state (northward facing upside down acacia stairs). How can I make this coordinate contain that block? (well, you need to be facing north and right click the top face of an adjacent block with such a stair)". That code needs to be written for every block that has custom placement like that. The question of "If block X is at coordinate Y, could I right click against that to place block Z on face D" is incredibly frustratingly not easily answerable. It all depends on external variables like the horizontal orientation of the player, and even the Y coordinate of the player (for some blocks like pistons and dispensers). \ No newline at end of file +One particularly annoying thing is needing to reimplement portions of Minecraft. For example, there is a bit of code that I can easily call that says "If I looked in this direction right now, and right clicked, what block precisely would it place". Because, well, Minecraft needs to decide what block appears if and when I right click. This works great for current Baritone and I can just call it without needing to worry. For this builder though, I need to do hypotheticals. "If I were standing here on top of this block (that doesn't exist yet), looking at this face of this block (which doesn't exist yet), and right clicked while holding this item (which I don't have), what block would appear?". Minecraft code isn't built like that. There are so many conditions based on Y hit, face hit, player orientation, etc. Just look at the trapdoor place logic to instantly die. So I need to reimplement all the code for "I desire this block state (northward facing upside down acacia stairs). How can I make this coordinate contain that block? (well, you need to be facing north and right click the top face of an adjacent block with such a stair)". That code needs to be written for every block that has custom placement like that. The question of "If block X is at coordinate Y, could I right click against that to place block Z on face D" is incredibly frustratingly not easily answerable. It all depends on external variables like the horizontal orientation of the player, and even the Y coordinate of the player (for some blocks like pistons and dispensers). + +I'm trying my best to minimize floating point calculations as much as possible. The fast case for the graph search should never use floating point. I'm too annoyed by even simple things like `> 1` being randomly wrong (with `0.99999` or `1.000001`). Also, no need to have path costs be floating point. Both path costs and player Y will be fixed point integers. Costs have a to-be-determined denominator, for player Y it's sixteen. \ No newline at end of file diff --git a/src/main/java/baritone/builder/Raytracer.java b/src/main/java/baritone/builder/Raytracer.java index 944f65810..376d7a5a7 100644 --- a/src/main/java/baritone/builder/Raytracer.java +++ b/src/main/java/baritone/builder/Raytracer.java @@ -210,11 +210,11 @@ public class Raytracer { return fast == null ? slow == null ? faster : slow : fast; } - private static void print(LongArrayList trace) { + public 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) { + public 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."); } @@ -347,13 +347,13 @@ public class Raytracer { 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 fracIfSkipX = 2; // just has to be strictly greater than 1, might as well just go up to the next int + double fracIfSkipY = 2; + double fracIfSkipZ = 2; double distanceFromStartToEndX = endX - startX; double distanceFromStartToEndY = endY - startY; double distanceFromStartToEndZ = endZ - startZ; - if (voxelEndX != voxelInX) { + if (voxelEndX != voxelInX) { // reminder to future self: don't "branchlessify" this, it's MUCH slower (pretty obviously, floating point div is much worse than a branch mispredict, but integer increment (like the other two removed branches) are cheap enough to be worth doing either way) fracIfSkipX = (nextIntegerX - startX) / distanceFromStartToEndX; } if (voxelEndY != voxelInY) { @@ -362,32 +362,35 @@ public class Raytracer { 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; + voxelInX = ((int) startX) - ((voxelEndX - voxelInX) >>> 31); // tested: faster to paste this 3 times with only one of the subtractions in each + voxelInY = ((int) startY); + voxelInZ = ((int) startZ); } else if (fracIfSkipY < fracIfSkipZ) { - dy = (voxelEndY - voxelInY) >>> 31; // we want dy=1 when endY < inY startX += distanceFromStartToEndX * fracIfSkipY; startY = nextIntegerY; startZ += distanceFromStartToEndZ * fracIfSkipY; + voxelInX = ((int) startX); + voxelInY = ((int) startY) - ((voxelEndY - voxelInY) >>> 31); + voxelInZ = ((int) startZ); } else { - dz = (voxelEndZ - voxelInZ) >>> 31; startX += distanceFromStartToEndX * fracIfSkipZ; startY += distanceFromStartToEndY * fracIfSkipZ; startZ = nextIntegerZ; + voxelInX = ((int) startX); + voxelInY = ((int) startY); + voxelInZ = ((int) startZ) - ((voxelEndZ - voxelInZ) >>> 31); } - - 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 rayTraceZoomyAlternate(double startX, double startY, double startZ, double endX, double endY, double endZ, long againstPos) { + return rayTraceZoomy(startX, startY, startZ, endX, endY, endZ, againstPos); + } } diff --git a/src/main/java/baritone/builder/ReachabilityCache.java b/src/main/java/baritone/builder/ReachabilityCache.java new file mode 100644 index 000000000..f1f56313e --- /dev/null +++ b/src/main/java/baritone/builder/ReachabilityCache.java @@ -0,0 +1,59 @@ +/* + * 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 . + */ + +package baritone.builder; + +import baritone.api.utils.BetterBlockPos; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; + +public class ReachabilityCache implements IReachabilityProvider { + + private final Long2ObjectOpenHashMap playerPositionToBlock; + + public ReachabilityCache(DependencyGraphScaffoldingOverlay overlay, PlayerReachSphere sphere) throws SchematicIsTooDenseForThisToMakeSenseException { + playerPositionToBlock = new Long2ObjectOpenHashMap<>(); + int maxReasonableCacheSize = overlay.bounds().size; + int[] cnt = {0}; + overlay.forEachReal(blockPos -> { // by only iterating through real blocks, this will be a much faster and better option for sparse schematics (e.g. map art) + for (long offset : sphere.positions) { + long playerEyeVoxel = (blockPos + offset) & BetterBlockPos.POST_ADDITION_MASK; + if (overlay.bounds().inRangePos(playerEyeVoxel)) { + LongArrayList blocks = playerPositionToBlock.get(playerEyeVoxel); + if (blocks == null) { + blocks = new LongArrayList(); + playerPositionToBlock.put(playerEyeVoxel, blocks); + } + blocks.add(blockPos); + if (cnt[0]++ > maxReasonableCacheSize) { + throw new SchematicIsTooDenseForThisToMakeSenseException(); // although, of course, it's perfectly possible for this to NOT be a good idea too + } + } + } + }); + } + + @Override + public LongList candidates(long playerEyeVoxel) { + return playerPositionToBlock.get(playerEyeVoxel); + } + + public static class SchematicIsTooDenseForThisToMakeSenseException extends RuntimeException { + + } +} diff --git a/src/main/java/baritone/builder/ReachabilityLive.java b/src/main/java/baritone/builder/ReachabilityLive.java new file mode 100644 index 000000000..fcd47ef96 --- /dev/null +++ b/src/main/java/baritone/builder/ReachabilityLive.java @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +package baritone.builder; + +import baritone.api.utils.BetterBlockPos; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; + +public class ReachabilityLive implements IReachabilityProvider { + + private final DependencyGraphScaffoldingOverlay overlay; + private final PlayerReachSphere sphere; + + public ReachabilityLive(DependencyGraphScaffoldingOverlay overlay, PlayerReachSphere sphere) { + this.overlay = overlay; + this.sphere = sphere; + } + + @Override + public LongList candidates(long playerEyeVoxel) { + LongArrayList ret = new LongArrayList(); + for (long offset : sphere.positions) { + long block = (playerEyeVoxel + offset) & BetterBlockPos.POST_ADDITION_MASK; + if (overlay.bounds().inRangePos(block)) { + ret.add(block); + } + } + return ret; + } +} diff --git a/src/main/java/baritone/builder/Testing.java b/src/main/java/baritone/builder/Testing.java index d73b79c78..6cf816e1b 100644 --- a/src/main/java/baritone/builder/Testing.java +++ b/src/main/java/baritone/builder/Testing.java @@ -23,6 +23,7 @@ import baritone.utils.BlockStateInterface; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; @@ -267,5 +268,24 @@ public class Testing { }*/ } + public static class BlockStateLookupHelper { + + private static Reference2IntOpenHashMap states = new Reference2IntOpenHashMap<>(); + + static { + states.defaultReturnValue(-1); // normal default is 0 + } + + public static int lookupBlockState(IBlockState state) { + int stateMaybe = states.getInt(state); + if (stateMaybe > 0) { + return stateMaybe; + } + int realState = Block.BLOCK_STATE_IDS.get(state); // uses slow REAL hashcode that walks through the Map of properties, gross + states.put(state, realState); + return realState; + } + } + }