|  |  |  | @@ -25,14 +25,13 @@ import baritone.api.pathing.goals.GoalGetToBlock; | 
		
	
		
			
				|  |  |  |  | import baritone.api.process.IBuilderProcess; | 
		
	
		
			
				|  |  |  |  | import baritone.api.process.PathingCommand; | 
		
	
		
			
				|  |  |  |  | import baritone.api.process.PathingCommandType; | 
		
	
		
			
				|  |  |  |  | import baritone.api.utils.BetterBlockPos; | 
		
	
		
			
				|  |  |  |  | import baritone.api.utils.ISchematic; | 
		
	
		
			
				|  |  |  |  | import baritone.api.utils.Rotation; | 
		
	
		
			
				|  |  |  |  | import baritone.api.utils.RotationUtils; | 
		
	
		
			
				|  |  |  |  | import baritone.api.utils.*; | 
		
	
		
			
				|  |  |  |  | import baritone.api.utils.input.Input; | 
		
	
		
			
				|  |  |  |  | import baritone.pathing.movement.CalculationContext; | 
		
	
		
			
				|  |  |  |  | import baritone.pathing.movement.Movement; | 
		
	
		
			
				|  |  |  |  | import baritone.pathing.movement.MovementHelper; | 
		
	
		
			
				|  |  |  |  | import baritone.utils.BaritoneProcessHelper; | 
		
	
		
			
				|  |  |  |  | import baritone.utils.BlockStateInterface; | 
		
	
		
			
				|  |  |  |  | import baritone.utils.PathingCommandContext; | 
		
	
		
			
				|  |  |  |  | import baritone.utils.schematic.Schematic; | 
		
	
		
			
				|  |  |  |  | import net.minecraft.block.state.IBlockState; | 
		
	
	
		
			
				
					
					|  |  |  | @@ -44,8 +43,7 @@ import net.minecraft.nbt.CompressedStreamTools; | 
		
	
		
			
				|  |  |  |  | import net.minecraft.nbt.NBTTagCompound; | 
		
	
		
			
				|  |  |  |  | import net.minecraft.util.EnumFacing; | 
		
	
		
			
				|  |  |  |  | import net.minecraft.util.Tuple; | 
		
	
		
			
				|  |  |  |  | import net.minecraft.util.math.BlockPos; | 
		
	
		
			
				|  |  |  |  | import net.minecraft.util.math.Vec3i; | 
		
	
		
			
				|  |  |  |  | import net.minecraft.util.math.*; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | import java.io.File; | 
		
	
		
			
				|  |  |  |  | import java.io.FileInputStream; | 
		
	
	
		
			
				
					
					|  |  |  | @@ -65,6 +63,7 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro | 
		
	
		
			
				|  |  |  |  |     private String name; | 
		
	
		
			
				|  |  |  |  |     private ISchematic schematic; | 
		
	
		
			
				|  |  |  |  |     private Vec3i origin; | 
		
	
		
			
				|  |  |  |  |     private int ticks; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public boolean build(String schematicFile) { | 
		
	
		
			
				|  |  |  |  |         File file = new File(new File(Minecraft.getMinecraft().gameDir, "schematics"), schematicFile); | 
		
	
	
		
			
				
					
					|  |  |  | @@ -145,6 +144,125 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro | 
		
	
		
			
				|  |  |  |  |         return Optional.empty(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public class Placement { | 
		
	
		
			
				|  |  |  |  |         final int hotbarSelection; | 
		
	
		
			
				|  |  |  |  |         final BlockPos placeAgainst; | 
		
	
		
			
				|  |  |  |  |         final EnumFacing side; | 
		
	
		
			
				|  |  |  |  |         final Rotation rot; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         public Placement(int hotbarSelection, BlockPos placeAgainst, EnumFacing side, Rotation rot) { | 
		
	
		
			
				|  |  |  |  |             this.hotbarSelection = hotbarSelection; | 
		
	
		
			
				|  |  |  |  |             this.placeAgainst = placeAgainst; | 
		
	
		
			
				|  |  |  |  |             this.side = side; | 
		
	
		
			
				|  |  |  |  |             this.rot = rot; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public Optional<Placement> searchForPlacables(BuilderCalculationContext bcc) { | 
		
	
		
			
				|  |  |  |  |         BetterBlockPos center = ctx.playerFeet(); | 
		
	
		
			
				|  |  |  |  |         for (int dx = -5; dx <= 5; dx++) { | 
		
	
		
			
				|  |  |  |  |             for (int dy = -5; dy <= 1; dy++) { | 
		
	
		
			
				|  |  |  |  |                 for (int dz = -5; dz <= 5; dz++) { | 
		
	
		
			
				|  |  |  |  |                     int x = center.x + dx; | 
		
	
		
			
				|  |  |  |  |                     int y = center.y + dy; | 
		
	
		
			
				|  |  |  |  |                     int z = center.z + dz; | 
		
	
		
			
				|  |  |  |  |                     IBlockState desired = bcc.getSchematic(x, y, z); | 
		
	
		
			
				|  |  |  |  |                     if (desired == null) { | 
		
	
		
			
				|  |  |  |  |                         continue; // irrelevant | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                     IBlockState curr = bcc.bsi.get0(x, y, z); | 
		
	
		
			
				|  |  |  |  |                     if (MovementHelper.isReplacable(x, y, z, curr, bcc.bsi) && !valid(curr, desired)) { | 
		
	
		
			
				|  |  |  |  |                         if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() == Blocks.AIR) { | 
		
	
		
			
				|  |  |  |  |                             continue; | 
		
	
		
			
				|  |  |  |  |                         } | 
		
	
		
			
				|  |  |  |  |                         Optional<Placement> opt = possibleToPlace(desired, x, y, z, bcc.bsi); | 
		
	
		
			
				|  |  |  |  |                         if (opt.isPresent()) { | 
		
	
		
			
				|  |  |  |  |                             return opt; | 
		
	
		
			
				|  |  |  |  |                         } | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return Optional.empty(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public Optional<Placement> possibleToPlace(IBlockState toPlace, int x, int y, int z, BlockStateInterface bsi) { | 
		
	
		
			
				|  |  |  |  |         for (EnumFacing against : EnumFacing.values()) { | 
		
	
		
			
				|  |  |  |  |             BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).offset(against); | 
		
	
		
			
				|  |  |  |  |             IBlockState placeAgainstState = bsi.get0(placeAgainstPos); | 
		
	
		
			
				|  |  |  |  |             if (MovementHelper.isReplacable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { | 
		
	
		
			
				|  |  |  |  |                 continue; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             if (!ctx.world().mayPlace(toPlace.getBlock(), new BetterBlockPos(x, y, z), false, against, null)) { | 
		
	
		
			
				|  |  |  |  |                 continue; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             AxisAlignedBB aabb = placeAgainstState.getBoundingBox(ctx.world(), placeAgainstPos); | 
		
	
		
			
				|  |  |  |  |             for (Vec3d placementMultiplier : aabbSideMultipliers(against)) { | 
		
	
		
			
				|  |  |  |  |                 double placeX = placeAgainstPos.x + aabb.minX * placementMultiplier.x + aabb.maxX * (1 - placementMultiplier.x); | 
		
	
		
			
				|  |  |  |  |                 double placeY = placeAgainstPos.y + aabb.minY * placementMultiplier.y + aabb.maxY * (1 - placementMultiplier.y); | 
		
	
		
			
				|  |  |  |  |                 double placeZ = placeAgainstPos.z + aabb.minZ * placementMultiplier.z + aabb.maxZ * (1 - placementMultiplier.z); | 
		
	
		
			
				|  |  |  |  |                 Rotation rot = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(placeX, placeY, placeZ)); | 
		
	
		
			
				|  |  |  |  |                 RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot, ctx.playerController().getBlockReachDistance()); | 
		
	
		
			
				|  |  |  |  |                 if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK && result.getBlockPos().equals(placeAgainstPos) && result.sideHit == against.getOpposite()) { | 
		
	
		
			
				|  |  |  |  |                     OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, rot); | 
		
	
		
			
				|  |  |  |  |                     if (hotbar.isPresent()) { | 
		
	
		
			
				|  |  |  |  |                         return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return Optional.empty(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public OptionalInt hasAnyItemThatWouldPlace(IBlockState desired, RayTraceResult result, Rotation rot) { | 
		
	
		
			
				|  |  |  |  |         for (int i = 0; i < 9; i++) { | 
		
	
		
			
				|  |  |  |  |             ItemStack stack = ctx.player().inventory.mainInventory.get(i); | 
		
	
		
			
				|  |  |  |  |             if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) { | 
		
	
		
			
				|  |  |  |  |                 continue; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             float originalYaw = ctx.player().rotationYaw; | 
		
	
		
			
				|  |  |  |  |             float originalPitch = ctx.player().rotationPitch; | 
		
	
		
			
				|  |  |  |  |             // the state depends on the facing of the player sometimes | 
		
	
		
			
				|  |  |  |  |             ctx.player().rotationYaw = rot.getYaw(); | 
		
	
		
			
				|  |  |  |  |             ctx.player().rotationPitch = rot.getPitch(); | 
		
	
		
			
				|  |  |  |  |             IBlockState wouldBePlaced = ((ItemBlock) stack.getItem()).getBlock().getStateForPlacement( | 
		
	
		
			
				|  |  |  |  |                     ctx.world(), | 
		
	
		
			
				|  |  |  |  |                     result.getBlockPos().offset(result.sideHit), | 
		
	
		
			
				|  |  |  |  |                     result.sideHit, | 
		
	
		
			
				|  |  |  |  |                     (float) result.hitVec.x - result.getBlockPos().getX(), // as in PlayerControllerMP | 
		
	
		
			
				|  |  |  |  |                     (float) result.hitVec.y - result.getBlockPos().getY(), | 
		
	
		
			
				|  |  |  |  |                     (float) result.hitVec.z - result.getBlockPos().getZ(), | 
		
	
		
			
				|  |  |  |  |                     stack.getItem().getMetadata(stack.getMetadata()), | 
		
	
		
			
				|  |  |  |  |                     ctx.player() | 
		
	
		
			
				|  |  |  |  |             ); | 
		
	
		
			
				|  |  |  |  |             ctx.player().rotationYaw = originalYaw; | 
		
	
		
			
				|  |  |  |  |             ctx.player().rotationPitch = originalPitch; | 
		
	
		
			
				|  |  |  |  |             if (valid(wouldBePlaced, desired)) { | 
		
	
		
			
				|  |  |  |  |                 return OptionalInt.of(i); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return OptionalInt.empty(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     private static Vec3d[] aabbSideMultipliers(EnumFacing side) { | 
		
	
		
			
				|  |  |  |  |         switch (side) { | 
		
	
		
			
				|  |  |  |  |             case UP: | 
		
	
		
			
				|  |  |  |  |                 return new Vec3d[]{new Vec3d(0.5, 1, 0.5)}; | 
		
	
		
			
				|  |  |  |  |             case DOWN: | 
		
	
		
			
				|  |  |  |  |                 return new Vec3d[]{new Vec3d(0.5, 0, 0.5)}; | 
		
	
		
			
				|  |  |  |  |             case NORTH: | 
		
	
		
			
				|  |  |  |  |             case SOUTH: | 
		
	
		
			
				|  |  |  |  |             case EAST: | 
		
	
		
			
				|  |  |  |  |             case WEST: | 
		
	
		
			
				|  |  |  |  |                 double x = side.getXOffset() == 0 ? 0.5 : (1 + side.getXOffset()) / 2D; | 
		
	
		
			
				|  |  |  |  |                 double z = side.getZOffset() == 0 ? 0.5 : (1 + side.getZOffset()) / 2D; | 
		
	
		
			
				|  |  |  |  |                 return new Vec3d[]{new Vec3d(x, 0.25, z), new Vec3d(x, 0.75, z)}; | 
		
	
		
			
				|  |  |  |  |             default: // null | 
		
	
		
			
				|  |  |  |  |                 throw new NullPointerException(); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     @Override | 
		
	
		
			
				|  |  |  |  |     public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { | 
		
	
		
			
				|  |  |  |  |         // TODO somehow tell inventorybehavior what we'd like to have on the hotbar | 
		
	
	
		
			
				
					
					|  |  |  | @@ -161,6 +279,11 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro | 
		
	
		
			
				|  |  |  |  |             onLostControl(); | 
		
	
		
			
				|  |  |  |  |             return null; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         if (baritone.getInputOverrideHandler().isInputForcedDown(Input.CLICK_LEFT)) { | 
		
	
		
			
				|  |  |  |  |             ticks = 5; | 
		
	
		
			
				|  |  |  |  |         } else { | 
		
	
		
			
				|  |  |  |  |             ticks--; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         Optional<Tuple<BetterBlockPos, Rotation>> toBreak = toBreakNearPlayer(bcc); | 
		
	
		
			
				|  |  |  |  |         baritone.getInputOverrideHandler().clearAllKeys(); | 
		
	
		
			
				|  |  |  |  |         if (toBreak.isPresent() && isSafeToCancel && ctx.player().onGround) { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -170,11 +293,22 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro | 
		
	
		
			
				|  |  |  |  |             BetterBlockPos pos = toBreak.get().getFirst(); | 
		
	
		
			
				|  |  |  |  |             baritone.getLookBehavior().updateTarget(rot, true); | 
		
	
		
			
				|  |  |  |  |             MovementHelper.switchToBestToolFor(ctx, bcc.get(pos)); | 
		
	
		
			
				|  |  |  |  |             if (Objects.equals(ctx.objectMouseOver().getBlockPos(), rot) || ctx.playerRotations().isReallyCloseTo(rot)) { | 
		
	
		
			
				|  |  |  |  |             if (Objects.equals(ctx.objectMouseOver().getBlockPos(), pos) || ctx.playerRotations().isReallyCloseTo(rot)) { | 
		
	
		
			
				|  |  |  |  |                 baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         Optional<Placement> toPlace = searchForPlacables(bcc); | 
		
	
		
			
				|  |  |  |  |         if (toPlace.isPresent() && isSafeToCancel && ctx.player().onGround && ticks <= 0) { | 
		
	
		
			
				|  |  |  |  |             Rotation rot = toPlace.get().rot; | 
		
	
		
			
				|  |  |  |  |             baritone.getLookBehavior().updateTarget(rot, true); | 
		
	
		
			
				|  |  |  |  |             ctx.player().inventory.currentItem = toPlace.get().hotbarSelection; | 
		
	
		
			
				|  |  |  |  |             baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); | 
		
	
		
			
				|  |  |  |  |             if ((Objects.equals(ctx.objectMouseOver().getBlockPos(), toPlace.get().placeAgainst) && ctx.objectMouseOver().sideHit.equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) { | 
		
	
		
			
				|  |  |  |  |                 baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         Goal goal = assemble(bcc); | 
		
	
		
			
				|  |  |  |  |         if (goal == null) { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -241,7 +375,7 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro | 
		
	
		
			
				|  |  |  |  |         List<IBlockState> approxPlacable = placable(); | 
		
	
		
			
				|  |  |  |  |         List<BetterBlockPos> placable = incorrectPositions.stream().filter(pos -> bcc.bsi.get0(pos).getBlock() == Blocks.AIR && approxPlacable.contains(bcc.getSchematic(pos.x, pos.y, pos.z))).collect(Collectors.toList()); | 
		
	
		
			
				|  |  |  |  |         Goal[] toBreak = incorrectPositions.stream().filter(pos -> bcc.bsi.get0(pos).getBlock() != Blocks.AIR).map(GoalBreak::new).toArray(Goal[]::new); | 
		
	
		
			
				|  |  |  |  |         Goal[] toPlace = placable.stream().filter(pos -> !placable.contains(pos.down()) && !placable.contains(pos.down(2))).map(GoalPlace::new).toArray(Goal[]::new); | 
		
	
		
			
				|  |  |  |  |         Goal[] toPlace = placable.stream().filter(pos -> !placable.contains(pos.down()) && !placable.contains(pos.down(2))).map(pos -> placementgoal(pos, bcc)).toArray(Goal[]::new); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if (toPlace.length != 0) { | 
		
	
		
			
				|  |  |  |  |             return new JankyGoalComposite(new GoalComposite(toPlace), new GoalComposite(toBreak)); | 
		
	
	
		
			
				
					
					|  |  |  | @@ -290,6 +424,43 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public Goal placementgoal(BlockPos pos, BuilderCalculationContext bcc) { | 
		
	
		
			
				|  |  |  |  |         boolean allowSameLevel = ctx.world().getBlockState(pos.up()).getBlock() != Blocks.AIR; | 
		
	
		
			
				|  |  |  |  |         for (EnumFacing facing : Movement.HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP) { | 
		
	
		
			
				|  |  |  |  |             if (MovementHelper.canPlaceAgainst(ctx, pos.offset(facing)) && ctx.world().mayPlace(bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ()).getBlock(), pos, false, facing, null)) { | 
		
	
		
			
				|  |  |  |  |                 return new GoalAdjacent(pos, allowSameLevel); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return new GoalPlace(pos); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public static class GoalAdjacent extends GoalGetToBlock { | 
		
	
		
			
				|  |  |  |  |         boolean allowSameLevel; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         public GoalAdjacent(BlockPos pos, boolean allowSameLevel) { | 
		
	
		
			
				|  |  |  |  |             super(pos); | 
		
	
		
			
				|  |  |  |  |             this.allowSameLevel = allowSameLevel; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         public boolean isInGoal(int x, int y, int z) { | 
		
	
		
			
				|  |  |  |  |             if (x == this.x && y == this.y && z == this.z) { | 
		
	
		
			
				|  |  |  |  |                 return false; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             if (!allowSameLevel && y == this.y - 1) { | 
		
	
		
			
				|  |  |  |  |                 return false; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             if (y < this.y - 1) { | 
		
	
		
			
				|  |  |  |  |                 return false; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             return super.isInGoal(x, y, z); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         public double heuristic(int x, int y, int z) { | 
		
	
		
			
				|  |  |  |  |             // prioritize lower y coordinates | 
		
	
		
			
				|  |  |  |  |             return this.y * 100 + super.heuristic(x, y, z); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     public static class GoalPlace extends GoalBlock { | 
		
	
		
			
				|  |  |  |  |         public GoalPlace(BlockPos placeAt) { | 
		
	
		
			
				|  |  |  |  |             super(placeAt.up()); | 
		
	
	
		
			
				
					
					|  |  |  |   |