From efb0afcd6c96f6a32eb2a87f7c087bc9a6e06bc1 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Mon, 10 May 2021 02:18:59 -0700 Subject: [PATCH] switch to builder pattern for block state cached data --- src/main/java/baritone/Baritone.java | 3 + .../builder/BlockStateCachedData.java | 122 +++------- .../builder/BlockStateCachedDataBuilder.java | 212 ++++++++++++++++++ .../builder/BlockStatePlacementOption.java | 19 +- .../builder/IBlockStateDataProvider.java | 11 +- src/main/java/baritone/builder/Main.java | 3 + .../builder/PlaceOrderDependencyGraph.java | 4 +- .../mc/BlockStatePropertiesExtractor.java | 29 ++- .../mc/VanillaBlockStateDataProvider.java | 6 +- 9 files changed, 290 insertions(+), 119 deletions(-) create mode 100644 src/main/java/baritone/builder/BlockStateCachedDataBuilder.java diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index b9c1fd606..02b71c6fa 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -126,6 +126,9 @@ public class Baritone implements IBaritone { Main.main(); } catch (InterruptedException e) { e.printStackTrace(); + } catch (Throwable th) { + th.printStackTrace(); + throw th; } } diff --git a/src/main/java/baritone/builder/BlockStateCachedData.java b/src/main/java/baritone/builder/BlockStateCachedData.java index 706cdbf03..f8f685d42 100644 --- a/src/main/java/baritone/builder/BlockStateCachedData.java +++ b/src/main/java/baritone/builder/BlockStateCachedData.java @@ -18,7 +18,7 @@ package baritone.builder; import javax.annotation.Nullable; -import java.util.*; +import java.util.List; /** * Information about an IBlockState @@ -27,72 +27,48 @@ 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, Half.EITHER, false); + private static final BlockStateCachedData[] PER_STATE = Main.DATA_PROVIDER.allNullable(); + public static final BlockStateCachedData SCAFFOLDING = new BlockStateCachedData(new BlockStateCachedDataBuilder().normalFullBlock()); - public final boolean canWalkOn; + public final boolean fullyWalkableTop; public final boolean isAir; + + public final boolean mustSneakWhenPlacingAgainstMe; private final boolean[] presentsTopHalfFaceForPlacement; private final boolean[] presentsBottomHalfFaceForPlacement; - private final List options; + + public final List options; public static BlockStateCachedData get(int state) { return PER_STATE[state]; } - 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(); - } - } + public BlockStateCachedData(BlockStateCachedDataBuilder builder) { + builder.sanityCheck(); + this.isAir = builder.isAir(); + this.fullyWalkableTop = builder.isFullyWalkableTop(); + this.mustSneakWhenPlacingAgainstMe = builder.isMustSneakWhenPlacingAgainstMe(); + this.options = builder.howCanIBePlaced(); - private void setupFacesPresented(boolean canPlaceAgainstAtAll, Half half) { - if (!canPlaceAgainstAtAll) { - return; - } - Arrays.fill(presentsBottomHalfFaceForPlacement, true); - Arrays.fill(presentsTopHalfFaceForPlacement, true); - // TODO support case for placing against nub of stair on the one faced side - switch (half) { - case EITHER: { - return; - } - case TOP: { - // i am a top slab, or an upside down stair - presentsBottomHalfFaceForPlacement[Face.DOWN.index] = false; - presentsTopHalfFaceForPlacement[Face.DOWN.index] = false; - for (Face face : Face.HORIZONTALS) { - presentsBottomHalfFaceForPlacement[face.index] = false; // top slab = can't place against the bottom half - } - break; - } - case BOTTOM: { - // i am a bottom slab, or an normal stair - presentsBottomHalfFaceForPlacement[Face.UP.index] = false; - presentsTopHalfFaceForPlacement[Face.UP.index] = false; - for (Face face : Face.HORIZONTALS) { - presentsTopHalfFaceForPlacement[face.index] = false; // bottom slab = can't place against the top half - } - break; - } - } + boolean[][] presented = builder.facesIPresentForPlacementAgainst(); + this.presentsTopHalfFaceForPlacement = presented[1]; + this.presentsBottomHalfFaceForPlacement = presented[0]; } @Nullable - private PlaceAgainstData presentsFace(Face face, Half half) { - if ((face == Face.UP || face == Face.DOWN) && half != Half.EITHER) { + public PlaceAgainstData tryAgainstMe(BlockStatePlacementOption placement) { + if (Main.fakePlacementForPerformanceTesting) { + return Main.RAND.nextInt(10) < 8 ? PlaceAgainstData.EITHER : null; + } + + Face myFace = placement.against.opposite(); + Half theirHalf = placement.half; + if ((myFace == Face.UP || myFace == Face.DOWN) && theirHalf != 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); + boolean top = presentsTopHalfFaceForPlacement[myFace.index] && (theirHalf == Half.EITHER || theirHalf == Half.TOP); + boolean bottom = presentsBottomHalfFaceForPlacement[myFace.index] && (theirHalf == Half.EITHER || theirHalf == 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) { intersectedHalf = Half.EITHER; @@ -105,48 +81,4 @@ public final class BlockStateCachedData { } return PlaceAgainstData.get(intersectedHalf, mustSneakWhenPlacingAgainstMe); } - - private List calcOptions(boolean canPlaceAgainstAtAll, Half half) { - if (canPlaceAgainstAtAll) { - List ret = new ArrayList<>(); - for (Face face : Face.VALUES) { - if (Main.STRICT_Y && face == Face.UP) { - continue; - } - Half overrideHalf = half; - if (face == Face.DOWN) { - if (half == Half.TOP) { - continue; - } else { - overrideHalf = Half.EITHER; - } - } - if (face == Face.UP) { - if (half == Half.BOTTOM) { - continue; - } else { - overrideHalf = Half.EITHER; - } - } - ret.add(BlockStatePlacementOption.get(face, overrideHalf, Optional.empty())); - } - return ret; - } - return Collections.emptyList(); - } - - - @Nullable - public PlaceAgainstData canBeDoneAgainstMe(BlockStatePlacementOption placement) { - if (Main.fakePlacementForPerformanceTesting) { - return Main.RAND.nextInt(10) < 8 ? PlaceAgainstData.EITHER : null; - } - - Face myFace = placement.against.opposite(); - return presentsFace(myFace, placement.half); - } - - public List placementOptions() { - return options; - } } diff --git a/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java b/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java new file mode 100644 index 000000000..4d9dee372 --- /dev/null +++ b/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java @@ -0,0 +1,212 @@ +/* + * 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 java.util.*; + +public class BlockStateCachedDataBuilder { + + // should all these be optionals? like maybe Boolean? that start as null? and each has to be set explicitly? + private boolean isAir; + private boolean canPlaceAgainstMe; + private boolean fullyWalkableTop; + private boolean mustSneakWhenPlacingAgainstMe; + /** + * Examples: + *

+ * Upside down stairs must be placed against TOP + *

+ * Bottom slabs must be placed against BOTTOM + *

+ * Normal blocks must be placed against EITHER + */ + private Half mustBePlacedAgainst = Half.EITHER; + private boolean stair; + private Face playerMustBeFacingInOrderToPlaceMe; + private double height = Double.NaN; + + public BlockStateCachedDataBuilder() { + } + + public BlockStateCachedDataBuilder normalFullBlock() { + return fullyWalkableTop().height(1).canPlaceAgainstMe(); + } + + public BlockStateCachedDataBuilder stair(boolean rightsideUp, Face facing) { + stair = true; + playerMustBeFacingInOrderToPlaceMe = facing; + return slab(rightsideUp); + } + + public BlockStateCachedDataBuilder slab(boolean bottom) { + mustBePlacedAgainst = bottom ? Half.BOTTOM : Half.TOP; + return bottom ? this : fullyWalkableTop().height(1).canPlaceAgainstMe(); + } + + /** + * Really just air. This is a fully open block that won't collide with object mouse over raytrace. + */ + public BlockStateCachedDataBuilder air() { + isAir = true; + mustBePlacedAgainst = null; + return this; + } + + public boolean isAir() { + return isAir; + } + + /** + * does the top face of this block fully support the player from 0.0,0.0 to 1.0,1.0? true for most normal blocks. false for, for example, fences + */ + public BlockStateCachedDataBuilder fullyWalkableTop() { + fullyWalkableTop = true; + return this; + } + + public boolean isFullyWalkableTop() { + return fullyWalkableTop; + } + + public BlockStateCachedDataBuilder height(double y) { + if (!isFullyWalkableTop()) { + throw new IllegalStateException(); + } + height = y; + return this; + } + + public double supportedPlayerY() { // e.g. slabs are 0.5, soul sand is 0.875, normal blocks are 1, fences are 1.5 + if (!isFullyWalkableTop() || Double.isNaN(height)) { + throw new IllegalStateException(); // e.g. rightside-up stairs aren't fully supporting of the player so this doesn't count for them + } + return height; + } + + public BlockStateCachedDataBuilder mustSneakWhenPlacingAgainstMe() { + mustSneakWhenPlacingAgainstMe = true; + return this; + } + + public boolean isMustSneakWhenPlacingAgainstMe() { + return mustSneakWhenPlacingAgainstMe; + } + + public BlockStateCachedDataBuilder canPlaceAgainstMe() { + canPlaceAgainstMe = true; + return this; + } + + public List howCanIBePlaced() { + if (mustBePlacedAgainst == null) { + return Collections.emptyList(); + } + List ret = new ArrayList<>(); + for (Face face : Face.VALUES) { + if (Main.STRICT_Y && face == Face.UP) { + continue; + } + if (playerMustBeFacingInOrderToPlaceMe == face.opposite()) { // obv, this won't happen if playerMustBeFacing is null + continue; + } + Half overrideHalf = mustBePlacedAgainst; + if (face == Face.DOWN) { + if (mustBePlacedAgainst == Half.TOP) { + continue; + } else { + overrideHalf = Half.EITHER; + } + } + if (face == Face.UP) { + if (mustBePlacedAgainst == Half.BOTTOM) { + continue; + } else { + overrideHalf = Half.EITHER; + } + } + ret.add(BlockStatePlacementOption.get(face, overrideHalf, Optional.ofNullable(playerMustBeFacingInOrderToPlaceMe))); + } + return Collections.unmodifiableList(ret); + } + + public boolean[][] facesIPresentForPlacementAgainst() { + boolean[] presentsBottomHalfFaceForPlacement = new boolean[Face.VALUES.length]; + boolean[] presentsTopHalfFaceForPlacement = new boolean[Face.VALUES.length]; + if (canPlaceAgainstMe) { + Arrays.fill(presentsBottomHalfFaceForPlacement, true); + Arrays.fill(presentsTopHalfFaceForPlacement, true); + switch (mustBePlacedAgainst) { + case EITHER: { + break; + } + case TOP: { + // i am a top slab, or an upside down stair + presentsBottomHalfFaceForPlacement[Face.DOWN.index] = false; + presentsTopHalfFaceForPlacement[Face.DOWN.index] = false; + for (Face face : Face.HORIZONTALS) { + if (stair && face == playerMustBeFacingInOrderToPlaceMe) { + continue; // little nub of the stair on the faced side + } + presentsBottomHalfFaceForPlacement[face.index] = false; // top slab = can't place against the bottom half + } + break; + } + case BOTTOM: { + // i am a bottom slab, or an normal stair + presentsBottomHalfFaceForPlacement[Face.UP.index] = false; + presentsTopHalfFaceForPlacement[Face.UP.index] = false; + for (Face face : Face.HORIZONTALS) { + if (stair && face == playerMustBeFacingInOrderToPlaceMe) { + continue; // little nub of the stair on the faced side + } + presentsTopHalfFaceForPlacement[face.index] = false; // bottom slab = can't place against the top half + } + break; + } + } + } + return new boolean[][]{presentsBottomHalfFaceForPlacement, presentsTopHalfFaceForPlacement}; + } + + public void sanityCheck() { + if (isAir()) { + if (!howCanIBePlaced().isEmpty()) { + throw new IllegalStateException(); + } + if (isFullyWalkableTop()) { + throw new IllegalStateException(); + } + } + if (mustBePlacedAgainst == null ^ isAir()) { + throw new IllegalStateException(); + } + if (isMustSneakWhenPlacingAgainstMe() && mustBePlacedAgainst != Half.EITHER) { + throw new IllegalArgumentException(); + } + if (stair ^ (playerMustBeFacingInOrderToPlaceMe != null && mustBePlacedAgainst != Half.EITHER)) { + throw new IllegalStateException(); + } + if (isFullyWalkableTop() == Double.isNaN(height)) { + throw new IllegalStateException(); + } + } + + static { + new BlockStateCachedDataBuilder().sanityCheck(); + } +} diff --git a/src/main/java/baritone/builder/BlockStatePlacementOption.java b/src/main/java/baritone/builder/BlockStatePlacementOption.java index bc790b9fc..ad244b036 100644 --- a/src/main/java/baritone/builder/BlockStatePlacementOption.java +++ b/src/main/java/baritone/builder/BlockStatePlacementOption.java @@ -57,8 +57,8 @@ public class BlockStatePlacementOption { */ private static final double LOOSE_CENTER_DISTANCE = 0.15; - public List 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) + public List computeTraceOptions(PlaceAgainstData placingAgainst, int playerSupportingX, double playerEyeY, int playerSupportingZ, PlayerVantage vantage, double blockReachDistance) { + if (placingAgainst.half != half && half != Half.EITHER) { // narrowing is ok (EITHER -> TOP/BOTTOM) but widening isn't (TOP/BOTTOM -> EITHER) throw new IllegalStateException(); } List acceptableVantages = new ArrayList<>(); @@ -95,14 +95,13 @@ public class BlockStatePlacementOption { .map(playerEyeXZ -> new Vec3d(playerEyeXZ.x, playerEyeY, playerEyeXZ.z)) .flatMap(eye -> Stream.of(FACE_PROJECTION_CACHE[against.index]) - .filter(this::hitOk) + .filter(hit -> hitOk(placingAgainst, hit)) .filter(hit -> eye.distSq(hit) < blockReachDistance * blockReachDistance) .filter(hit -> directionOk(eye, hit)) .>>map(hit -> () -> Raytracer.runTrace(eye, placeAgainstPos, against.opposite(), hit)) ) .collect(Collectors.toList()) - // TODO switch back to parallelStream - .stream() // wrap it like this because flatMap forces .sequential() on the interior child stream, defeating the point + .parallelStream() // 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) @@ -110,15 +109,15 @@ public class BlockStatePlacementOption { .collect(Collectors.toList())); } - private boolean hitOk(Vec3d hit) { - if (half == Half.EITHER) { + private static boolean hitOk(PlaceAgainstData placingAgainst, Vec3d hit) { + if (placingAgainst.half == Half.EITHER) { return true; } else if (hit.y == 0.1) { - return half == Half.BOTTOM; + return placingAgainst.half == Half.BOTTOM; } else if (hit.y == 0.5) { return false; } else if (hit.y == 0.9) { - return half == Half.TOP; + return placingAgainst.half == Half.TOP; } else { throw new IllegalStateException(); } @@ -226,7 +225,7 @@ public class BlockStatePlacementOption { 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 traces = BlockStatePlacementOption.get(Face.NORTH, Half.BOTTOM, Optional.of(playerFacing)).computeTraceOptions(Half.BOTTOM, 1, 1.62, 0, vantage, 4); + List traces = BlockStatePlacementOption.get(Face.NORTH, Half.BOTTOM, Optional.of(playerFacing)).computeTraceOptions(PlaceAgainstData.get(Half.BOTTOM, false), 1, 1.62, 0, vantage, 4); sanity.append(traces.size()); sanity.append(" "); if (!traces.isEmpty()) { diff --git a/src/main/java/baritone/builder/IBlockStateDataProvider.java b/src/main/java/baritone/builder/IBlockStateDataProvider.java index 7d9518e25..c65e18071 100644 --- a/src/main/java/baritone/builder/IBlockStateDataProvider.java +++ b/src/main/java/baritone/builder/IBlockStateDataProvider.java @@ -18,16 +18,21 @@ package baritone.builder; import java.util.Arrays; +import java.util.Optional; public interface IBlockStateDataProvider { int numStates(); - BlockStateCachedData get(int i); + Optional getBuilder(int i); - default BlockStateCachedData[] all() { + default BlockStateCachedData getNullable(int i) { + return getBuilder(i).map(BlockStateCachedData::new).orElse(null); + } + + default BlockStateCachedData[] allNullable() { BlockStateCachedData[] ret = new BlockStateCachedData[numStates()]; - Arrays.setAll(ret, this::get); + Arrays.setAll(ret, this::getNullable); return ret; } } diff --git a/src/main/java/baritone/builder/Main.java b/src/main/java/baritone/builder/Main.java index 3355f7622..bf1481de5 100644 --- a/src/main/java/baritone/builder/Main.java +++ b/src/main/java/baritone/builder/Main.java @@ -156,6 +156,9 @@ public class Main { .collect(Collectors.toList()).parallelStream() .forEach(x -> System.out.println(x + "")); } + { + BlockStateCachedData.get(0); + } { BlockStatePlacementOption.sanityCheck(); } diff --git a/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java b/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java index 43fc2953b..2b2b9919d 100644 --- a/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java +++ b/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java @@ -42,7 +42,7 @@ public class PlaceOrderDependencyGraph { } private void compute(long pos) { - for (BlockStatePlacementOption option : data(pos).placementOptions()) { + for (BlockStatePlacementOption option : data(pos).options) { if (Main.STRICT_Y && option.against == Face.UP) { throw new IllegalStateException(); } @@ -53,7 +53,7 @@ public class PlaceOrderDependencyGraph { } else { against = BlockStateCachedData.SCAFFOLDING; } - if (against.canBeDoneAgainstMe(option) != null) { + if (against.tryAgainstMe(option) != null) { edges.set(bitIndex(pos, option.against)); } } diff --git a/src/main/java/baritone/builder/mc/BlockStatePropertiesExtractor.java b/src/main/java/baritone/builder/mc/BlockStatePropertiesExtractor.java index 3c6e7b549..d71202c8b 100644 --- a/src/main/java/baritone/builder/mc/BlockStatePropertiesExtractor.java +++ b/src/main/java/baritone/builder/mc/BlockStatePropertiesExtractor.java @@ -17,12 +17,15 @@ package baritone.builder.mc; -import baritone.builder.BlockStateCachedData; -import baritone.builder.Half; +import baritone.builder.BlockStateCachedDataBuilder; +import baritone.builder.Face; import net.minecraft.block.Block; import net.minecraft.block.BlockAir; +import net.minecraft.block.BlockSlab; +import net.minecraft.block.BlockStairs; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; +import net.minecraft.util.EnumFacing; /** * I expect this class to get extremely complicated. @@ -31,13 +34,27 @@ import net.minecraft.init.Blocks; */ public class BlockStatePropertiesExtractor { - public static BlockStateCachedData getData(IBlockState state) { + public static BlockStateCachedDataBuilder getData(IBlockState state) { Block block = state.getBlock(); + BlockStateCachedDataBuilder builder = new BlockStateCachedDataBuilder(); if (block instanceof BlockAir) { - return new BlockStateCachedData(true, false, false, Half.EITHER, false); + return builder.air(); } - boolean normal = block == Blocks.COBBLESTONE || block == Blocks.DIRT; - return new BlockStateCachedData(false, normal, normal, Half.EITHER, false); + if (block instanceof BlockStairs) { + boolean rightsideUp = state.getValue(BlockStairs.HALF) == BlockStairs.EnumHalf.BOTTOM; // true if normal stair, false if upside down stair + EnumFacing facing = state.getValue(BlockStairs.FACING); + return builder.stair(rightsideUp, Face.fromMC(facing)); + } + if (block instanceof BlockSlab) { + if (((BlockSlab) block).isDouble()) { + return builder.normalFullBlock(); + } + return builder.slab(state.getValue(BlockSlab.HALF) == BlockSlab.EnumBlockHalf.BOTTOM); + } + if (block == Blocks.COBBLESTONE || block == Blocks.DIRT) { + builder.normalFullBlock(); + } + return builder; } } diff --git a/src/main/java/baritone/builder/mc/VanillaBlockStateDataProvider.java b/src/main/java/baritone/builder/mc/VanillaBlockStateDataProvider.java index 39560fe91..8c4824001 100644 --- a/src/main/java/baritone/builder/mc/VanillaBlockStateDataProvider.java +++ b/src/main/java/baritone/builder/mc/VanillaBlockStateDataProvider.java @@ -17,7 +17,7 @@ package baritone.builder.mc; -import baritone.builder.BlockStateCachedData; +import baritone.builder.BlockStateCachedDataBuilder; import baritone.builder.IBlockStateDataProvider; import net.minecraft.block.Block; @@ -31,7 +31,7 @@ public class VanillaBlockStateDataProvider implements IBlockStateDataProvider { } @Override - public BlockStateCachedData get(int i) { - return Optional.ofNullable(Block.BLOCK_STATE_IDS.getByValue(i)).map(BlockStatePropertiesExtractor::getData).orElse(null); + public Optional getBuilder(int i) { + return Optional.ofNullable(Block.BLOCK_STATE_IDS.getByValue(i)).map(BlockStatePropertiesExtractor::getData); } }