misc bounds benchmark, property extractor, scaffolder
This commit is contained in:
@@ -31,7 +31,7 @@ public final class BlockStateCachedData {
|
||||
public static final BlockStateCachedData SCAFFOLDING = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(1).canPlaceAgainstMe());
|
||||
|
||||
public final boolean fullyWalkableTop;
|
||||
public final Integer collisionHeightBlips;
|
||||
private final int collisionHeightBlips;
|
||||
public final boolean isAir;
|
||||
|
||||
public final boolean collidesWithPlayer;
|
||||
@@ -52,7 +52,11 @@ public final class BlockStateCachedData {
|
||||
this.isAir = builder.isAir();
|
||||
this.fullyWalkableTop = builder.isFullyWalkableTop();
|
||||
this.collidesWithPlayer = builder.isCollidesWithPlayer();
|
||||
this.collisionHeightBlips = builder.collisionHeightBlips();
|
||||
if (collidesWithPlayer) {
|
||||
this.collisionHeightBlips = builder.collisionHeightBlips();
|
||||
} else {
|
||||
this.collisionHeightBlips = -1;
|
||||
}
|
||||
|
||||
this.mustSneakWhenPlacingAgainstMe = builder.isMustSneakWhenPlacingAgainstMe();
|
||||
this.options = Collections.unmodifiableList(builder.howCanIBePlaced());
|
||||
@@ -60,6 +64,13 @@ public final class BlockStateCachedData {
|
||||
this.againstMe = builder.placeAgainstMe();
|
||||
}
|
||||
|
||||
public int collisionHeightBlips() {
|
||||
if (Main.DEBUG && !collidesWithPlayer) { // confirmed and tested: when DEBUG is false, proguard removes this if in the first pass, then inlines the calls in the second pass, making this just as good as a field access in release builds
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return collisionHeightBlips;
|
||||
}
|
||||
|
||||
public boolean possibleAgainstMe(BlockStatePlacementOption placement) {
|
||||
if (Main.fakePlacementForPerformanceTesting) {
|
||||
return Main.RAND.nextInt(10) < 8;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class BlockStateCachedDataBuilder {
|
||||
private boolean fullyWalkableTop;
|
||||
private boolean collidesWithPlayer;
|
||||
private boolean mustSneakWhenPlacingAgainstMe;
|
||||
private boolean falling;
|
||||
private boolean mustBePlacedBottomToTop;
|
||||
/**
|
||||
* Examples:
|
||||
* <p>
|
||||
@@ -76,6 +76,15 @@ public class BlockStateCachedDataBuilder {
|
||||
return fullyWalkableTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* The highest collision extension of this block possible
|
||||
* <p>
|
||||
* For example, should be 1 for stairs, even though part of the top face is really 0.5
|
||||
* <p>
|
||||
* Should be 1 for top slabs
|
||||
* <p>
|
||||
* 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 = 1; h <= Blip.PER_BLOCK + Blip.HALF_BLOCK; h++) {
|
||||
if (y == h * Blip.RATIO) {
|
||||
@@ -131,8 +140,8 @@ public class BlockStateCachedDataBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder falling() {
|
||||
falling = true;
|
||||
public BlockStateCachedDataBuilder mustBePlacedBottomToTop() {
|
||||
mustBePlacedBottomToTop = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -166,7 +175,7 @@ public class BlockStateCachedDataBuilder {
|
||||
if (playerMustBeEntityFacingInOrderToPlaceMe == face) {
|
||||
continue;
|
||||
}
|
||||
if (falling && face != Face.DOWN) {
|
||||
if (mustBePlacedBottomToTop && face != Face.DOWN) {
|
||||
continue;
|
||||
}
|
||||
if (canOnlyPlaceAgainst != null && face != canOnlyPlaceAgainst) {
|
||||
@@ -214,6 +223,14 @@ public class BlockStateCachedDataBuilder {
|
||||
return new PlaceAgainstData(face, face.vertical ? Half.EITHER : mustBePlacedAgainst, mustSneakWhenPlacingAgainstMe);
|
||||
}
|
||||
|
||||
/**
|
||||
* The idea here is that I codify all my assumptions in one place instead of having ad hoc checks absolutely everywhere
|
||||
* <p>
|
||||
* Example: in PlayerPhysics, I made an assumption that a block will never have a collision block taller than 1.5 blocks (e.g. like a fence)
|
||||
* When I wrote the code that assumed that, I also added a check here to make sure every block is like that.
|
||||
* If, in some future update to Minecraft, mojang adds a block that's even taller than a fence, it will be caught here immediately, with a comment saying "playerphysics assumes this is never true"
|
||||
* This way, I'll know immediately, instead of pathing randomly trying to do something impossible with that new block and it being really confusing and annoying.
|
||||
*/
|
||||
public void sanityCheck() {
|
||||
if (isAir()) {
|
||||
if (!howCanIBePlaced().isEmpty()) {
|
||||
@@ -246,17 +263,10 @@ public class BlockStateCachedDataBuilder {
|
||||
if ((playerMustBeHorizontalFacingInOrderToPlaceMe != null || playerMustBeEntityFacingInOrderToPlaceMe != null) && mustBePlacedAgainst == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (isFullyWalkableTop() ^ collisionHeightBlips != null) {
|
||||
if (!isFullyWalkableTop() && collisionHeightBlips > Blip.PER_BLOCK) {
|
||||
// exception for fences, walls
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
if (collisionHeightBlips != null && (collisionHeightBlips > Blip.FULL_BLOCK + Blip.HALF_BLOCK || collisionHeightBlips <= 0)) { // playerphysics assumes this is never true
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (collidesWithPlayer && collisionHeightBlips == null) {
|
||||
if (collidesWithPlayer ^ collisionHeightBlips != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (fullyWalkableTop && !collidesWithPlayer) {
|
||||
|
||||
@@ -147,7 +147,7 @@ public class BlockStatePlacementOption {
|
||||
if (playerMustBeHorizontalFacing.isPresent()) {
|
||||
return eye.flatDirectionTo(hit) == playerMustBeHorizontalFacing.get();
|
||||
}
|
||||
if (playerMustBeEntityFacing.isPresent()) { // handle piston, dispenser, observer
|
||||
if (playerMustBeEntityFacing.isPresent()) { // handle piston, dispenser, dropper, observer
|
||||
if (!hit.inOriginUnitVoxel()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
@@ -156,7 +156,7 @@ public class BlockStatePlacementOption {
|
||||
double dx = Math.abs(eye.x - 0.5);
|
||||
double dz = Math.abs(eye.z - 0.5);
|
||||
if (dx < 2 - ENTITY_FACING_TOLERANCE && dz < 2 - ENTITY_FACING_TOLERANCE) { // < 1.99
|
||||
if (eye.y < 0) { // eye below placement level = it will be facing down, so this is only okay if we wantthat
|
||||
if (eye.y < 0) { // eye below placement level = it will be facing down, so this is only okay if we want that
|
||||
return entFace == Face.DOWN;
|
||||
}
|
||||
if (eye.y > 2) { // same for up, if y>2 then it will be facing up
|
||||
@@ -165,7 +165,7 @@ public class BlockStatePlacementOption {
|
||||
} else if (!(dx > 2 + ENTITY_FACING_TOLERANCE || dz > 2 + ENTITY_FACING_TOLERANCE)) { // > 2.01
|
||||
// this is the ambiguous case, because we are neither unambiguously both-within-2 (previous case), nor unambiguously either-above-two (this elseif condition).
|
||||
// UP/DOWN are impossible, but that's caught by flat check
|
||||
if (eye.y < 0 || eye.y > 2) {
|
||||
if (eye.y < 0 || eye.y > 2) { // this check is okay because player eye height is not an even multiple of blips, therefore there's no way for it to == 0 or == 2, so using > and < is safe
|
||||
return false; // anything that could cause up/down instead of horizontal is also not allowed sadly
|
||||
}
|
||||
} // else we are in unambiguous either-above-two, putting us in simple horizontal mode, so fallthrough to flat condition is correct, yay
|
||||
|
||||
@@ -61,10 +61,11 @@ public class CuboidBounds {
|
||||
}
|
||||
|
||||
public boolean inRange(int x, int y, int z) {
|
||||
throw new UnsupportedOperationException("ugh benchmark this tomorrow when im less tired");
|
||||
return inRangeBranchless(x, y, z);
|
||||
}
|
||||
|
||||
public boolean inRangeBranchy(int x, int y, int z) {
|
||||
@Deprecated
|
||||
public boolean inRangeBranchy(int x, int y, int z) { // benchmarked: approx 4x slower than branchless
|
||||
return (x >= 0) && (x < sizeX) && (y >= 0) && (y < sizeY) && (z >= 0) && (z < sizeZ);
|
||||
}
|
||||
|
||||
@@ -72,6 +73,18 @@ public class CuboidBounds {
|
||||
return (x | y | z | (sizeXMinusOne - x) | (sizeYMinusOne - y) | (sizeZMinusOne - z)) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeBranchless2(int x, int y, int z) {
|
||||
return (x | y | z | ((sizeX - 1) - x) | ((sizeY - 1) - y) | ((sizeZ - 1) - z)) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeBranchless3(int x, int y, int z) {
|
||||
return (x | y | z | (sizeX - (x + 1)) | (sizeY - (y + 1)) | (sizeZ - (z + 1))) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeBranchless4(int x, int y, int z) {
|
||||
return (x | y | z | ((sizeX - x) - 1) | ((sizeY - y) - 1) | ((sizeZ - z) - 1)) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeIndex(int index) {
|
||||
return (index | (sizeMinusOne - index)) >= 0;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ public class DependencyGraphAnalyzer {
|
||||
if (!locs.isEmpty()) {
|
||||
throw new IllegalStateException("Unplaceable from any side: " + cuteTrim(locs));
|
||||
}
|
||||
// TODO instead of cuteTrim have a like SpecificBlockPositionsImpossibleException that this throws, and then later, an enclosing function can give the option to reset those locations to air
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public interface IBlockStateDataProvider {
|
||||
|
||||
int numStates();
|
||||
@@ -27,7 +25,20 @@ public interface IBlockStateDataProvider {
|
||||
|
||||
default BlockStateCachedData[] allNullable() {
|
||||
BlockStateCachedData[] ret = new BlockStateCachedData[numStates()];
|
||||
Arrays.setAll(ret, this::getNullable);
|
||||
RuntimeException ex = null;
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
try {
|
||||
ret[i] = getNullable(i);
|
||||
} catch (RuntimeException e) {
|
||||
if (ex != null) {
|
||||
ex.printStackTrace(); // printstacktrace all but the one that we throw
|
||||
}
|
||||
ex = e;
|
||||
}
|
||||
}
|
||||
if (ex != null) {
|
||||
throw ex; // throw the last one
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +266,133 @@ public class Main {
|
||||
{
|
||||
DebugStates.debug();
|
||||
}
|
||||
{
|
||||
Random rand = new Random(5021);
|
||||
int trials = 10_000_000;
|
||||
int[] X = new int[trials];
|
||||
int[] Y = new int[trials];
|
||||
int[] Z = new int[trials];
|
||||
int sz = 10;
|
||||
CuboidBounds bounds = new CuboidBounds(sz, sz, sz);
|
||||
for (int i = 0; i < trials; i++) {
|
||||
for (int[] toAdd : new int[][]{X, Y, Z}) {
|
||||
toAdd[i] = rand.nextBoolean() ? rand.nextInt(sz) : rand.nextBoolean() ? -1 : sz;
|
||||
}
|
||||
}
|
||||
boolean[] a = new boolean[trials];
|
||||
boolean[] b = new boolean[trials];
|
||||
boolean[] c = new boolean[trials];
|
||||
boolean[] d = new boolean[trials];
|
||||
boolean[] e = new boolean[trials];
|
||||
for (int it = 0; it < 20; it++) {
|
||||
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
a[i] = bounds.inRangeBranchy(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchy took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
b[i] = bounds.inRangeBranchless(X[i], Y[i], Z[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 < trials; i++) {
|
||||
c[i] = bounds.inRangeBranchless2(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchless2 took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
d[i] = bounds.inRangeBranchless3(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchless3 took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
e[i] = bounds.inRangeBranchless4(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchless4 took " + (end - start) + "ms");
|
||||
}
|
||||
/*
|
||||
Branchless2 took 55ms
|
||||
Branchless3 took 53ms
|
||||
Branchless4 took 47ms
|
||||
Branchy took 137ms
|
||||
Branchless took 35ms
|
||||
Branchless2 took 36ms
|
||||
Branchless3 took 35ms
|
||||
Branchless4 took 41ms
|
||||
Branchy took 118ms
|
||||
Branchless took 33ms
|
||||
Branchless2 took 39ms
|
||||
Branchless3 took 36ms
|
||||
Branchless4 took 42ms
|
||||
Branchy took 125ms
|
||||
Branchless took 41ms
|
||||
Branchless2 took 45ms
|
||||
Branchless3 took 41ms
|
||||
Branchless4 took 45ms
|
||||
Branchy took 123ms
|
||||
Branchless took 38ms
|
||||
Branchless2 took 43ms
|
||||
Branchless3 took 35ms
|
||||
Branchless4 took 43ms
|
||||
Branchy took 117ms
|
||||
Branchless took 37ms
|
||||
Branchless2 took 42ms
|
||||
Branchless3 took 41ms
|
||||
Branchless4 took 45ms
|
||||
Branchy took 123ms
|
||||
Branchless took 35ms
|
||||
Branchless2 took 42ms
|
||||
Branchless3 took 38ms
|
||||
Branchless4 took 46ms
|
||||
Branchy took 126ms
|
||||
Branchless took 34ms
|
||||
Branchless2 took 47ms
|
||||
Branchless3 took 40ms
|
||||
Branchless4 took 47ms
|
||||
Branchy took 124ms
|
||||
*/
|
||||
|
||||
// 3 is better than 2 and 4 because of data dependency
|
||||
// the L1 cache fetch for this.sizeX can happen at the same time as "x+1" (which is an increment of an argument)
|
||||
// in other words: in options 2 and 4, the "+1" or "-1" has a data dependency on the RAM fetch for this.sizeX, but in option 3 alone, the +1 happens upon the argument x, which is likely in a register, meaning it can be pipelined in parallel with the L1 cache fetch for this.sizeX
|
||||
|
||||
}
|
||||
}
|
||||
/*{ // proguard test
|
||||
PlayerPhysics.determinePlayerRealSupport(BlockStateCachedData.get(69), BlockStateCachedData.get(420));
|
||||
PlayerPhysics.determinePlayerRealSupport(BlockStateCachedData.get(420), BlockStateCachedData.get(69));
|
||||
}*/
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,12 @@ public class PlaceAgainstData {
|
||||
this.top = top;
|
||||
this.bottom = bottom;
|
||||
this.hits = hits;
|
||||
if (!streamRelativeToMyself().allMatch(Vec3d::inOriginUnitVoxel)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!streamRelativeToPlace().allMatch(Vec3d::inOriginUnitVoxel)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public PlaceAgainstData(Face against, Half half, boolean mustSneak) {
|
||||
|
||||
@@ -26,21 +26,21 @@ public class PlayerPhysics {
|
||||
*/
|
||||
public static int determinePlayerRealSupport(BlockStateCachedData underneath, BlockStateCachedData within) {
|
||||
if (within.collidesWithPlayer) {
|
||||
if (underneath.collisionHeightBlips != null && underneath.collisionHeightBlips - Blip.FULL_BLOCK > within.collisionHeightBlips) { // TODO > or >=
|
||||
if (underneath.collidesWithPlayer && underneath.collisionHeightBlips() - Blip.FULL_BLOCK > within.collisionHeightBlips()) { // > because imagine something like slab on top of fence, we can walk on the slab even though the fence is equivalent height
|
||||
if (!underneath.fullyWalkableTop) {
|
||||
return -1;
|
||||
}
|
||||
return underneath.collisionHeightBlips - Blip.FULL_BLOCK; // this could happen if "underneath" is a fence and "within" is a carpet
|
||||
return underneath.collisionHeightBlips() - Blip.FULL_BLOCK; // this could happen if "underneath" is a fence and "within" is a carpet
|
||||
}
|
||||
if (!within.fullyWalkableTop || within.collisionHeightBlips >= Blip.FULL_BLOCK) {
|
||||
if (!within.fullyWalkableTop || within.collisionHeightBlips() >= Blip.FULL_BLOCK) {
|
||||
return -1;
|
||||
}
|
||||
return within.collisionHeightBlips;
|
||||
return within.collisionHeightBlips();
|
||||
} else {
|
||||
if (!underneath.fullyWalkableTop || underneath.collisionHeightBlips < Blip.FULL_BLOCK) {
|
||||
if (!underneath.fullyWalkableTop || underneath.collisionHeightBlips() < Blip.FULL_BLOCK) { // short circuit only calls collisionHeightBlips when fullyWalkableTop is true, so this is safe
|
||||
return -1;
|
||||
}
|
||||
return underneath.collisionHeightBlips - Blip.FULL_BLOCK;
|
||||
return underneath.collisionHeightBlips() - Blip.FULL_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +119,10 @@ public class PlayerPhysics {
|
||||
if (!D.collidesWithPlayer) {
|
||||
return Collision.FALL;
|
||||
}
|
||||
if (Main.DEBUG && D.collisionHeightBlips == null) {
|
||||
if (Main.DEBUG && D.collisionHeightBlips() >= Blip.FULL_BLOCK && D.fullyWalkableTop) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (Main.DEBUG && D.collisionHeightBlips >= Blip.FULL_BLOCK && D.fullyWalkableTop) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (D.collisionHeightBlips < Blip.FULL_BLOCK + feet) {
|
||||
if (D.collisionHeightBlips() < Blip.FULL_BLOCK + feet) {
|
||||
return Collision.FALL;
|
||||
} else {
|
||||
return Collision.BLOCKED;
|
||||
@@ -143,5 +140,6 @@ public class PlayerPhysics {
|
||||
VOXEL_UP, // if you hit W, you will end up at a position that's a bit higher, such that you'd determineRealPlayerSupport up by one (example: walking from a partial block to a full block or higher, e.g. half slab to full block, or soul sand to full block, or soul sand to full block+carpet on top)
|
||||
VOXEL_LEVEL, // if you hit W, you will end up at a similar position, such that you'd determineRealPlayerSupport at the same integer grid location (example: walking forward on level ground)
|
||||
FALL // if you hit W, you will not immediately collide with anything, at all, to the front or to the bottom (example: walking off a cliff)
|
||||
// TODO maybe we need another option that is like "you could do it, but you shouldn't". like, "if you hit W, you would walk forward, but you wouldn't like the outcome" such as cactus or lava or something
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,13 @@ public class Scaffolder {
|
||||
this.components = collapsedGraph.getComponents();
|
||||
this.componentLocations = collapsedGraph.getComponentLocations();
|
||||
|
||||
this.rootComponents = calcRoots();
|
||||
}
|
||||
|
||||
private List<CollapsedDependencyGraphComponent> calcRoots() {
|
||||
// since the components form a DAG (because all strongly connected components, and therefore all cycles, have been collapsed)
|
||||
// we can locate all root components by simply finding the ones with no incoming edges
|
||||
this.rootComponents = components
|
||||
return components
|
||||
.values()
|
||||
.stream()
|
||||
.filter(component -> component.getIncoming().isEmpty())
|
||||
@@ -62,6 +66,7 @@ public class Scaffolder {
|
||||
}
|
||||
|
||||
private void loop() {
|
||||
int cid = collapsedGraph.lastComponentID().getAsInt();
|
||||
CollapsedDependencyGraphComponent root = rootComponents.remove(rootComponents.size() - 1);
|
||||
if (!root.getIncoming().isEmpty()) {
|
||||
throw new IllegalStateException();
|
||||
@@ -84,6 +89,24 @@ public class Scaffolder {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < path.size() - 1; i++) {
|
||||
overlayGraph.enable(path.get(i).pos);
|
||||
}
|
||||
|
||||
int newCID = collapsedGraph.lastComponentID().getAsInt();
|
||||
for (int i = cid + 1; i <= newCID; i++) {
|
||||
if (components.get(i) != null && components.get(i).getIncoming().isEmpty()) {
|
||||
rootComponents.add(components.get(i));
|
||||
}
|
||||
}
|
||||
// this works because as we add new components and connect them up, we can say that
|
||||
rootComponents.removeIf(CollapsedDependencyGraphComponent::deleted);
|
||||
if (Main.DEBUG) {
|
||||
if (!rootComponents.equals(calcRoots())) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void walkAllDescendents(CollapsedDependencyGraphComponent root, Set<CollapsedDependencyGraphComponent> set) {
|
||||
|
||||
@@ -61,8 +61,10 @@ public class Vec3d {
|
||||
return new Vec3d(dst.x - x, dst.y - y, dst.z - z).flatDirection();
|
||||
}
|
||||
|
||||
private static final double AMBIGUITY_TOLERANCE = 0.01;
|
||||
|
||||
public Face flatDirection() {
|
||||
if (Math.abs(x) == Math.abs(z)) {
|
||||
if (Math.abs(Math.abs(x) - Math.abs(z)) < AMBIGUITY_TOLERANCE) {
|
||||
throw new IllegalStateException("ambiguous");
|
||||
}
|
||||
if (Math.abs(x) > Math.abs(z)) {
|
||||
|
||||
@@ -60,10 +60,10 @@ public class BlockStatePropertiesExtractor {
|
||||
};
|
||||
if (!rightsideUp) {
|
||||
stairBuilder.fullyWalkableTop();
|
||||
stairBuilder.collisionHeight(1);
|
||||
}
|
||||
return stairBuilder.mustBePlacedAgainst(rightsideUp ? Half.BOTTOM : Half.TOP)
|
||||
.collidesWithPlayer(true)
|
||||
.collisionHeight(1)
|
||||
.canPlaceAgainstMe()
|
||||
.playerMustBeHorizontalFacingInOrderToPlaceMe(facing);
|
||||
}
|
||||
@@ -93,7 +93,9 @@ public class BlockStatePropertiesExtractor {
|
||||
ret.add(BlockStatePlacementOption.get(facing.opposite(), bottom ? Half.BOTTOM : Half.TOP, Optional.empty(), Optional.empty()));
|
||||
return ret;
|
||||
}
|
||||
}.collidesWithPlayer(true); // dont allow walking on top of closed top-half trapdoor because redstone activation is scary and im not gonna predict it
|
||||
}
|
||||
.collisionHeight(1) // sometimes it can be 1, and for collision height we err on the side of max
|
||||
.collidesWithPlayer(true); // dont allow walking on top of closed top-half trapdoor because redstone activation is scary and im not gonna predict it
|
||||
}
|
||||
if (block instanceof BlockLog) {
|
||||
BlockLog.EnumAxis axis = state.getValue(BlockLog.LOG_AXIS);
|
||||
@@ -168,6 +170,7 @@ public class BlockStatePropertiesExtractor {
|
||||
|
||||
{
|
||||
if (block instanceof BlockContainer || block instanceof BlockWorkbench) {
|
||||
// TODO way more blocks have a right click action, e.g. redstone repeater, daylight sensor
|
||||
builder.mustSneakWhenPlacingAgainstMe();
|
||||
}
|
||||
}
|
||||
@@ -210,7 +213,7 @@ public class BlockStatePropertiesExtractor {
|
||||
}
|
||||
|
||||
if (block instanceof BlockFalling) {
|
||||
builder.falling();
|
||||
builder.mustBePlacedBottomToTop();
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +224,11 @@ public class BlockStatePropertiesExtractor {
|
||||
|
||||
// getStateForPlacement.against is the against face. placing a torch will have it as UP. placing a bottom slab will have it as UP. placing a top slab will have it as DOWN.
|
||||
|
||||
if (block instanceof BlockFence || (block instanceof BlockFenceGate && !state.getValue(BlockFenceGate.OPEN)) || block instanceof BlockWall) {
|
||||
builder.collisionHeight(1.5);
|
||||
fullyUnderstood = true;
|
||||
}
|
||||
|
||||
if (block instanceof BlockTorch) { // includes redstone torch
|
||||
builder.canOnlyPlaceAgainst(Face.fromMC(state.getValue(BlockTorch.FACING)).opposite());
|
||||
fullyUnderstood = true;
|
||||
@@ -228,6 +236,7 @@ public class BlockStatePropertiesExtractor {
|
||||
|
||||
if (block instanceof BlockShulkerBox) {
|
||||
builder.canOnlyPlaceAgainst(Face.fromMC(state.getValue(BlockShulkerBox.FACING)).opposite());
|
||||
builder.collisionHeight(1); // TODO should this be 1.5 because sometimes the shulker is open?
|
||||
fullyUnderstood = true;
|
||||
}
|
||||
|
||||
@@ -244,7 +253,9 @@ public class BlockStatePropertiesExtractor {
|
||||
|| block instanceof BlockRailBase
|
||||
|| block instanceof BlockFlower
|
||||
|| block instanceof BlockDeadBush
|
||||
|| block instanceof BlockMushroom
|
||||
) {
|
||||
builder.mustBePlacedBottomToTop();
|
||||
fullyUnderstood = true;
|
||||
}
|
||||
|
||||
@@ -254,8 +265,11 @@ public class BlockStatePropertiesExtractor {
|
||||
fullyUnderstood = true;
|
||||
}
|
||||
|
||||
if ((state.isBlockNormalCube() || block instanceof BlockGlass || block instanceof BlockStainedGlass) && !(block instanceof BlockMagma || block instanceof BlockSlime)) {
|
||||
builder.fullyWalkableTop().collisionHeight(1);
|
||||
if (state.isBlockNormalCube() || block instanceof BlockGlass || block instanceof BlockStainedGlass) {
|
||||
builder.collisionHeight(1);
|
||||
if (!(block instanceof BlockMagma || block instanceof BlockSlime)) {
|
||||
builder.fullyWalkableTop();
|
||||
}
|
||||
fullyUnderstood = true;
|
||||
}
|
||||
|
||||
@@ -272,6 +286,11 @@ public class BlockStatePropertiesExtractor {
|
||||
fullyUnderstood = true;
|
||||
}
|
||||
|
||||
if (block instanceof BlockGrassPath || block instanceof BlockFarmland) {
|
||||
builder.collisionHeight(0.9375);
|
||||
fullyUnderstood = true;
|
||||
}
|
||||
|
||||
|
||||
// TODO fully walkable top and height
|
||||
if (fullyUnderstood) {
|
||||
|
||||
@@ -103,6 +103,8 @@ public class DebugStates {
|
||||
props.put("placeme", "" + data.options.size());
|
||||
props.put("sneak", "" + data.mustSneakWhenPlacingAgainstMe);
|
||||
props.put("againstme", "" + Stream.of(data.againstMe).filter(Objects::nonNull).count());
|
||||
props.put("y", "" + data.collisionHeightBlips);
|
||||
if (data.collidesWithPlayer) {
|
||||
props.put("y", "" + data.collisionHeightBlips());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user