diff --git a/src/main/java/baritone/builder/Blip.java b/src/main/java/baritone/builder/Blip.java index c1a342d39..da7de9978 100644 --- a/src/main/java/baritone/builder/Blip.java +++ b/src/main/java/baritone/builder/Blip.java @@ -35,10 +35,11 @@ public class Blip { public static final double RATIO = 0.0625; public static final int HALF_BLOCK = 8; public static final int PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE = 28; - public static final int PLAYER_HEIGHT_SLIGHT_OVERESTIMATE = PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE + 1; + public static final int PLAYER_HEIGHT_SLIGHT_OVERESTIMATE = 29; public static final int TWO_BLOCKS = 2 * FULL_BLOCK; public static final int FEET_TO_EYE_APPROX = (int) (IPlayerContext.eyeHeight(false) / RATIO); public static final int JUMP = 20; // 1.25 + public static final int TALLEST_BLOCK = FULL_BLOCK + HALF_BLOCK; // 24 public static double playerEyeFromFeetBlips(int feetBlips, boolean sneaking) { return feetBlips * RATIO + IPlayerContext.eyeHeight(sneaking); @@ -46,7 +47,7 @@ public class Blip { static { double realPlayerHeight = 1.8; - if (PLAYER_HEIGHT_SLIGHT_OVERESTIMATE * RATIO <= realPlayerHeight || PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE * RATIO >= realPlayerHeight) { + if (PLAYER_HEIGHT_SLIGHT_OVERESTIMATE * RATIO <= realPlayerHeight || PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE * RATIO >= realPlayerHeight || PLAYER_HEIGHT_SLIGHT_OVERESTIMATE != PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE + 1) { throw new IllegalStateException(); } if (PER_BLOCK * RATIO != 1) { diff --git a/src/main/java/baritone/builder/BlockStateCachedData.java b/src/main/java/baritone/builder/BlockStateCachedData.java index d2095924d..39d3c62b0 100644 --- a/src/main/java/baritone/builder/BlockStateCachedData.java +++ b/src/main/java/baritone/builder/BlockStateCachedData.java @@ -30,7 +30,7 @@ public final class BlockStateCachedData { private static final BlockStateCachedData[] PER_STATE = Main.DATA_PROVIDER.allNullable(); public static final BlockStateCachedData SCAFFOLDING = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(1).canPlaceAgainstMe()); - public static final BlockStateCachedData AIR = PER_STATE[0]; + public static final BlockStateCachedData AIR = new BlockStateCachedData(new BlockStateCachedDataBuilder().setAir()); public static final BlockStateCachedData OUT_OF_BOUNDS = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).collisionHeight(1)); static { diff --git a/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java b/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java index ca15ccc41..ef54f2488 100644 --- a/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java +++ b/src/main/java/baritone/builder/BlockStateCachedDataBuilder.java @@ -86,7 +86,7 @@ public class BlockStateCachedDataBuilder { * Should be 1 for trapdoors because when they're open, they touch the top face of the voxel */ public BlockStateCachedDataBuilder collisionHeight(double y) { - for (int h = 0; h <= Blip.PER_BLOCK + Blip.HALF_BLOCK; h++) { + for (int h = 0; h <= Blip.TALLEST_BLOCK; h++) { // max height of if (y == h * Blip.RATIO) { collisionHeightBlips = h; return this; @@ -263,7 +263,7 @@ public class BlockStateCachedDataBuilder { if ((playerMustBeHorizontalFacingInOrderToPlaceMe != null || playerMustBeEntityFacingInOrderToPlaceMe != null) && mustBePlacedAgainst == null) { throw new IllegalStateException(); } - if (collisionHeightBlips != null && (collisionHeightBlips > Blip.FULL_BLOCK + Blip.HALF_BLOCK || collisionHeightBlips < 0)) { // playerphysics assumes this is never true + if (collisionHeightBlips != null && (collisionHeightBlips > Blip.TALLEST_BLOCK || collisionHeightBlips < 0)) { // playerphysics assumes this is never true throw new IllegalStateException(); } if (collidesWithPlayer ^ collisionHeightBlips != null) { diff --git a/src/main/java/baritone/builder/Column.java b/src/main/java/baritone/builder/Column.java index 6b3dd74c0..c354b5584 100644 --- a/src/main/java/baritone/builder/Column.java +++ b/src/main/java/baritone/builder/Column.java @@ -25,13 +25,14 @@ import static baritone.api.utils.BetterBlockPos.Y_MASK; import static baritone.api.utils.BetterBlockPos.Y_SHIFT; /** - * A mutable class representing a 1x5x1 column of blocks + * A mutable class representing a 1x6x1 column of blocks *
* Mutable because allocations are not on the table for the core solver loop */ public class Column { public long pos; + //public BlockStateCachedData underUnderneath; public BlockStateCachedData underneath; public BlockStateCachedData feet; public BlockStateCachedData head; @@ -42,13 +43,22 @@ public class Column { public void initFrom(long pos, WorldState worldState, SolverEngineInput engineInput) { this.pos = pos; + //this.underUnderneath = engineInput.at((pos + DOWN_2) & BetterBlockPos.POST_ADDITION_MASK, worldState); this.underneath = engineInput.at((pos + DOWN_1) & BetterBlockPos.POST_ADDITION_MASK, worldState); this.feet = engineInput.at(pos, worldState); this.head = engineInput.at((pos + UP_1) & BetterBlockPos.POST_ADDITION_MASK, worldState); this.above = engineInput.at((pos + UP_2) & BetterBlockPos.POST_ADDITION_MASK, worldState); this.aboveAbove = engineInput.at((pos + UP_3) & BetterBlockPos.POST_ADDITION_MASK, worldState); + init(); + } + + public void init() { this.voxelResidency = PlayerPhysics.canPlayerStand(underneath, feet); this.feetBlips = boxNullable(PlayerPhysics.determinePlayerRealSupportLevel(underneath, feet, voxelResidency)); + if (feetBlips != null && !playerCanExistAtFootBlip(feetBlips)) { // TODO is this the correct way to handle head collision? + voxelResidency = PlayerPhysics.VoxelResidency.IMPOSSIBLE_WITHOUT_SUFFOCATING; // TODO this is a misuse of this enum value i think + feetBlips = null; + } } public boolean playerCanExistAtFootBlip(int blipWithinFeet) { @@ -70,12 +80,16 @@ public class Column { return feetBlips != null; } + private static final long DOWN_2 = (Y_MASK - 1) << Y_SHIFT; private static final long DOWN_1 = Y_MASK << Y_SHIFT; private static final long UP_1 = 1L << Y_SHIFT; private static final long UP_2 = 2L << Y_SHIFT; private static final long UP_3 = 3L << Y_SHIFT; static { + if (DOWN_2 != BetterBlockPos.toLong(0, -2, 0)) { + throw new IllegalStateException(); + } if (DOWN_1 != BetterBlockPos.toLong(0, -1, 0)) { throw new IllegalStateException(); } @@ -85,7 +99,7 @@ public class Column { if (UP_2 != BetterBlockPos.toLong(0, 2, 0)) { throw new IllegalStateException(); } - if (UP_3 != BetterBlockPos.toLong(0, 4, 0)) { + if (UP_3 != BetterBlockPos.toLong(0, 3, 0)) { throw new IllegalStateException(); } } diff --git a/src/main/java/baritone/builder/PlayerPhysics.java b/src/main/java/baritone/builder/PlayerPhysics.java index ab62fb7df..54d55bc0d 100644 --- a/src/main/java/baritone/builder/PlayerPhysics.java +++ b/src/main/java/baritone/builder/PlayerPhysics.java @@ -93,6 +93,9 @@ public class PlayerPhysics { } public static Collision playerTravelCollides(Column within, Column into) { + if (Main.DEBUG && within.head.collidesWithPlayer) { + throw new IllegalStateException(); + } return playerTravelCollides(within.feetBlips, within.above, into.above, @@ -105,6 +108,89 @@ public class PlayerPhysics { into.aboveAbove); } + + /** + * Is there a movement (ascend, level, descend) that the player can do AND UNDO? + * This is basically the same as playerTravelCollides, but, it's okay to fall, but only as long as you don't fall further than what can be jumped back up + * + * @return the integer change in voxel Y, or null if there is no such option + */ + public static Integer bidirectionalPlayerTravel( + Column within, + Column into, + BlockStateCachedData underUnderneathInto, + BlockStateCachedData underUnderUnderneathInto + ) { + switch (playerTravelCollides(within, into)) { + + case BLOCKED: + return null; + + case JUMP_TO_VOXEL_TWO_UP: + return 2; + case VOXEL_UP: + case JUMP_TO_VOXEL_UP: + return 1; + case VOXEL_LEVEL: + case JUMP_TO_VOXEL_LEVEL: + return 0; + + case FALL: + if (Main.DEBUG && (canPlayerStand(into.underneath, into.feet) != VoxelResidency.FLOATING || !into.playerCanExistAtFootBlip(within.feetBlips))) { + throw new IllegalStateException(); + } + VoxelResidency downOne = canPlayerStand(underUnderneathInto, into.underneath); + switch (downOne) { + case PREVENTED_BY_UNDERNEATH: + case PREVENTED_BY_WITHIN: + return null; // a block we aren't allowed to stand on + case UNDERNEATH_PROTRUDES_AT_OR_ABOVE_FULL_BLOCK: + case STANDARD_WITHIN_SUPPORT: + int fallBy = within.feetBlips + Blip.FULL_BLOCK - determinePlayerRealSupportLevel(underUnderneathInto, into.underneath, downOne); + if (fallBy < 1) { + throw new IllegalStateException(); + } + if (fallBy <= Blip.JUMP) { + return -1; // we could jump back! yay! + } + return null; // one-way trip + default: + throw new IllegalStateException(); + case FLOATING: + break; + } + // ^ as shown, we are floating in downOne, so let's try downTwo + // but first let's make sure it's possible + int highestBlipsWithinUnderUnderUnder = Blip.FULL_BLOCK - 1; // 15 + int highestJumpFromTwoUnder = highestBlipsWithinUnderUnderUnder + Blip.JUMP; // 35 + int highestJumpIntoThisVoxel = highestJumpFromTwoUnder - Blip.TWO_BLOCKS; // 3 + if (within.feetBlips > highestJumpIntoThisVoxel) { + return null; + } + VoxelResidency downTwo = canPlayerStand(underUnderUnderneathInto, underUnderneathInto); + switch (downTwo) { + case UNDERNEATH_PROTRUDES_AT_OR_ABOVE_FULL_BLOCK: // tallest block is 24 blips, which is less than 29, so this case is impossible + case PREVENTED_BY_UNDERNEATH: + case PREVENTED_BY_WITHIN: + case FLOATING: + return null; + case STANDARD_WITHIN_SUPPORT: + int fallBy = within.feetBlips + Blip.TWO_BLOCKS - determinePlayerRealSupportLevel(underUnderUnderneathInto, underUnderneathInto, downTwo); + if (fallBy < Blip.FULL_BLOCK + 1) { + throw new IllegalStateException(); + } + if (fallBy <= Blip.JUMP) { + return -2; // we could jump back! yay! + } + return null; // one-way trip + default: + throw new IllegalStateException(); + } + default: + throw new IllegalStateException(); + } + } + /** * "Can the player walk forwards without needing to break anything?" *
@@ -150,7 +236,7 @@ public class PlayerPhysics {
return Collision.JUMP_TO_VOXEL_TWO_UP;
}
}
- if (alreadyWithinU && A.collidesWithPlayer) {
+ if (alreadyWithinU && A.collidesWithPlayer) { // now that voxel two up, the result within A, is eliminated, we can't proceed if A would block at head level
return Collision.BLOCKED; // we are too tall. bonk!
}
// D cannot prevent us from doing anything because it cant be higher than 1.5. therefore, makes sense to check CB before DC.
@@ -169,7 +255,7 @@ public class PlayerPhysics {
return Collision.JUMP_TO_VOXEL_UP;
} // else this is possible!
if (Main.DEBUG && (U.collidesWithPlayer || A.collidesWithPlayer || !alreadyWithinU)) {
- // must already be colliding with U because in order for this step to be even possible, feet must be at least HALF_BLOCK
+ // must already be colliding with U because in order for this non-jump voxel-up step to be even possible, feet must be at least HALF_BLOCK
throw new IllegalStateException();
}
// B can collide with player here, such as if X is soul sand, C is full block, and B is carpet
@@ -177,7 +263,7 @@ public class PlayerPhysics {
}
// voxelUp is impossible. pessimistically, this means B is colliding. optimistically, this means B and C are air.
if (B.collidesWithPlayer) {
- // AB can no longer be possible since it was checked with E and F
+ // voxel up and voxel two up are both eliminated, so...
return Collision.BLOCKED; // we have ruled out stepping on top of B, so now if B is still colliding there is no way forward
}
int stayLevel = determinePlayerRealSupportLevel(D, C);
@@ -192,10 +278,11 @@ public class PlayerPhysics {
}
}
if (stayLevel > couldStepUpTo) { // staying within the same voxel means that a jump will always succeed
+ // aka. the jump might headbonk but not in a way that prevents the action. also TODO this is an example of where Collision.INADVISABLE could come in such as if E/F/U/A were like lava or something
return Collision.JUMP_TO_VOXEL_LEVEL;
}
- return Collision.VOXEL_LEVEL;
- }
+ return Collision.VOXEL_LEVEL; // btw it's possible that stayLevel < feetBlips, since a small fall is sorta ignored and treated as a VOXEL_LEVEL
+ } // voxel_level (C within, D underneath) is eliminated, only remaining possibilities are blocked or fall
if (C.collidesWithPlayer) {
return Collision.BLOCKED;
}
@@ -212,7 +299,7 @@ public class PlayerPhysics {
}
}
- public static boolean protrudesIntoThirdBlock(int feet) {
+ public static boolean protrudesIntoThirdBlock(int feet) { // only false for feet between 0 and 3. true for 4 and up
return feet > Blip.TWO_BLOCKS - Blip.PLAYER_HEIGHT_SLIGHT_OVERESTIMATE; // > and not >= because the player height is a slight overestimate
}
@@ -220,10 +307,14 @@ public class PlayerPhysics {
if (Blip.PLAYER_HEIGHT_SLIGHT_OVERESTIMATE >= Blip.TWO_BLOCKS || Blip.PLAYER_HEIGHT_SLIGHT_OVERESTIMATE + Blip.HALF_BLOCK <= Blip.TWO_BLOCKS) {
throw new IllegalStateException("Assumptions made in playerTravelCollides");
}
- int maxFeet = Blip.FULL_BLOCK - 1;
- int couldJumpUpTo = maxFeet + Blip.JUMP;
- int maxWithinAB = couldJumpUpTo - Blip.TWO_BLOCKS;
+ if (Blip.TALLEST_BLOCK - Blip.FULL_BLOCK + Blip.JUMP - Blip.TWO_BLOCKS >= 0) {
+ throw new IllegalStateException("Assumption made in bidirectionalPlayerTravel");
+ }
+ int maxFeet = Blip.FULL_BLOCK - 1; // 15
+ int couldJumpUpTo = maxFeet + Blip.JUMP; // 35
+ int maxWithinAB = couldJumpUpTo - Blip.TWO_BLOCKS; // 3
if (protrudesIntoThirdBlock(maxWithinAB)) {
+ // btw this is literally only 1 blip away from being true lol
throw new IllegalStateException("Oh no, if this is true then playerTravelCollides needs to check another layer above EF");
}
}
@@ -281,7 +372,7 @@ public class PlayerPhysics {
}
VoxelResidency res = canPlayerStand(engineInput.at(under, worldState), engineInput.at(support, worldState));
if (Main.DEBUG && descent == 0 && res != VoxelResidency.FLOATING) {
- throw new IllegalStateException(); // CD shouldn't collide, it should be D and the one beneath...
+ throw new IllegalStateException(); // CD shouldn't collide, it should be D and the one beneath... (playerTravelCollides would have returned BLOCKED or VOXEL_LEVEL)
}
switch (res) {
case FLOATING:
@@ -292,9 +383,6 @@ public class PlayerPhysics {
case UNDERNEATH_PROTRUDES_AT_OR_ABOVE_FULL_BLOCK:
case STANDARD_WITHIN_SUPPORT:
// found our landing spot
- if (Main.DEBUG && descent <= 0) {
- throw new IllegalStateException();
- }
return descent;
default:
throw new IllegalStateException();
diff --git a/src/test/java/baritone/builder/NavigableSurfaceTest.java b/src/test/java/baritone/builder/NavigableSurfaceTest.java
index fd6f5de92..754fc9bec 100644
--- a/src/test/java/baritone/builder/NavigableSurfaceTest.java
+++ b/src/test/java/baritone/builder/NavigableSurfaceTest.java
@@ -825,6 +825,8 @@ public class NavigableSurfaceTest {
"XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|X XXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXX X\n" +
"XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXX \n" +
"XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX\n";
+ // basically the story is that i saw THIS ^^^ weird ass shape, then i spent the next few hours figuring out exactly what happened and convincing myself that it actually is accurate and I don't have any bugs causing this incorrectly
+ // because i mean what are the odds lol, it looks crazy
assertEquals(shouldBeWeird, reportAllFourWalls(surface3));
}
diff --git a/src/test/java/baritone/builder/PlayerPhysicsTest.java b/src/test/java/baritone/builder/PlayerPhysicsTest.java
new file mode 100644
index 000000000..083c7193a
--- /dev/null
+++ b/src/test/java/baritone/builder/PlayerPhysicsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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