Repository: cabaletta/baritone Branch: 1.19.4 Commit: d3c170afaec7 Files: 396 Total size: 1.7 MB Directory structure: gitextract_izjt0185/ ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug.md │ │ ├── question.md │ │ └── suggestion.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── gradle_build.yml │ └── run_tests.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitmessage ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── FEATURES.md ├── LICENSE ├── README.md ├── SETUP.md ├── USAGE.md ├── build.gradle ├── buildSrc/ │ ├── .gitignore │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── baritone/ │ └── gradle/ │ ├── task/ │ │ ├── BaritoneGradleTask.java │ │ ├── CreateDistTask.java │ │ └── ProguardTask.java │ └── util/ │ └── Determinizer.java ├── fabric/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── resources/ │ └── fabric.mod.json ├── forge/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── java/ │ │ └── baritone/ │ │ └── launch/ │ │ └── BaritoneForgeModXD.java │ └── resources/ │ ├── META-INF/ │ │ └── mods.toml │ └── pack.mcmeta ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── scripts/ │ └── proguard.pro ├── settings.gradle ├── src/ │ ├── api/ │ │ └── java/ │ │ └── baritone/ │ │ └── api/ │ │ ├── BaritoneAPI.java │ │ ├── IBaritone.java │ │ ├── IBaritoneProvider.java │ │ ├── Settings.java │ │ ├── behavior/ │ │ │ ├── IBehavior.java │ │ │ ├── ILookBehavior.java │ │ │ ├── IPathingBehavior.java │ │ │ └── look/ │ │ │ ├── IAimProcessor.java │ │ │ └── ITickableAimProcessor.java │ │ ├── cache/ │ │ │ ├── IBlockTypeAccess.java │ │ │ ├── ICachedRegion.java │ │ │ ├── ICachedWorld.java │ │ │ ├── IWaypoint.java │ │ │ ├── IWaypointCollection.java │ │ │ ├── IWorldData.java │ │ │ ├── IWorldProvider.java │ │ │ ├── IWorldScanner.java │ │ │ └── Waypoint.java │ │ ├── command/ │ │ │ ├── Command.java │ │ │ ├── IBaritoneChatControl.java │ │ │ ├── ICommand.java │ │ │ ├── ICommandSystem.java │ │ │ ├── argparser/ │ │ │ │ ├── IArgParser.java │ │ │ │ └── IArgParserManager.java │ │ │ ├── argument/ │ │ │ │ ├── IArgConsumer.java │ │ │ │ └── ICommandArgument.java │ │ │ ├── datatypes/ │ │ │ │ ├── BlockById.java │ │ │ │ ├── EntityClassById.java │ │ │ │ ├── ForAxis.java │ │ │ │ ├── ForBlockOptionalMeta.java │ │ │ │ ├── ForDirection.java │ │ │ │ ├── ForWaypoints.java │ │ │ │ ├── IDatatype.java │ │ │ │ ├── IDatatypeContext.java │ │ │ │ ├── IDatatypeFor.java │ │ │ │ ├── IDatatypePost.java │ │ │ │ ├── IDatatypePostFunction.java │ │ │ │ ├── ItemById.java │ │ │ │ ├── NearbyPlayer.java │ │ │ │ ├── RelativeBlockPos.java │ │ │ │ ├── RelativeCoordinate.java │ │ │ │ ├── RelativeFile.java │ │ │ │ ├── RelativeGoal.java │ │ │ │ ├── RelativeGoalBlock.java │ │ │ │ ├── RelativeGoalXZ.java │ │ │ │ └── RelativeGoalYLevel.java │ │ │ ├── exception/ │ │ │ │ ├── CommandErrorMessageException.java │ │ │ │ ├── CommandException.java │ │ │ │ ├── CommandInvalidArgumentException.java │ │ │ │ ├── CommandInvalidStateException.java │ │ │ │ ├── CommandInvalidTypeException.java │ │ │ │ ├── CommandNoParserForTypeException.java │ │ │ │ ├── CommandNotEnoughArgumentsException.java │ │ │ │ ├── CommandNotFoundException.java │ │ │ │ ├── CommandTooManyArgumentsException.java │ │ │ │ ├── CommandUnhandledException.java │ │ │ │ └── ICommandException.java │ │ │ ├── helpers/ │ │ │ │ ├── Paginator.java │ │ │ │ └── TabCompleteHelper.java │ │ │ ├── manager/ │ │ │ │ └── ICommandManager.java │ │ │ └── registry/ │ │ │ └── Registry.java │ │ ├── event/ │ │ │ ├── events/ │ │ │ │ ├── BlockChangeEvent.java │ │ │ │ ├── BlockInteractEvent.java │ │ │ │ ├── ChatEvent.java │ │ │ │ ├── ChunkEvent.java │ │ │ │ ├── PacketEvent.java │ │ │ │ ├── PathEvent.java │ │ │ │ ├── PlayerUpdateEvent.java │ │ │ │ ├── RenderEvent.java │ │ │ │ ├── RotationMoveEvent.java │ │ │ │ ├── SprintStateEvent.java │ │ │ │ ├── TabCompleteEvent.java │ │ │ │ ├── TickEvent.java │ │ │ │ ├── WorldEvent.java │ │ │ │ └── type/ │ │ │ │ ├── Cancellable.java │ │ │ │ ├── EventState.java │ │ │ │ ├── ICancellable.java │ │ │ │ └── Overrideable.java │ │ │ └── listener/ │ │ │ ├── AbstractGameEventListener.java │ │ │ ├── IEventBus.java │ │ │ └── IGameEventListener.java │ │ ├── pathing/ │ │ │ ├── calc/ │ │ │ │ ├── IPath.java │ │ │ │ ├── IPathFinder.java │ │ │ │ └── IPathingControlManager.java │ │ │ ├── goals/ │ │ │ │ ├── Goal.java │ │ │ │ ├── GoalAxis.java │ │ │ │ ├── GoalBlock.java │ │ │ │ ├── GoalComposite.java │ │ │ │ ├── GoalGetToBlock.java │ │ │ │ ├── GoalInverted.java │ │ │ │ ├── GoalNear.java │ │ │ │ ├── GoalRunAway.java │ │ │ │ ├── GoalStrictDirection.java │ │ │ │ ├── GoalTwoBlocks.java │ │ │ │ ├── GoalXZ.java │ │ │ │ └── GoalYLevel.java │ │ │ ├── movement/ │ │ │ │ ├── ActionCosts.java │ │ │ │ ├── IMovement.java │ │ │ │ └── MovementStatus.java │ │ │ └── path/ │ │ │ └── IPathExecutor.java │ │ ├── process/ │ │ │ ├── IBaritoneProcess.java │ │ │ ├── IBuilderProcess.java │ │ │ ├── ICustomGoalProcess.java │ │ │ ├── IElytraProcess.java │ │ │ ├── IExploreProcess.java │ │ │ ├── IFarmProcess.java │ │ │ ├── IFollowProcess.java │ │ │ ├── IGetToBlockProcess.java │ │ │ ├── IMineProcess.java │ │ │ ├── PathingCommand.java │ │ │ └── PathingCommandType.java │ │ ├── schematic/ │ │ │ ├── AbstractSchematic.java │ │ │ ├── CompositeSchematic.java │ │ │ ├── CompositeSchematicEntry.java │ │ │ ├── FillSchematic.java │ │ │ ├── ISchematic.java │ │ │ ├── ISchematicSystem.java │ │ │ ├── IStaticSchematic.java │ │ │ ├── MaskSchematic.java │ │ │ ├── MirroredSchematic.java │ │ │ ├── ReplaceSchematic.java │ │ │ ├── RotatedSchematic.java │ │ │ ├── ShellSchematic.java │ │ │ ├── SubstituteSchematic.java │ │ │ ├── WallsSchematic.java │ │ │ ├── format/ │ │ │ │ └── ISchematicFormat.java │ │ │ └── mask/ │ │ │ ├── AbstractMask.java │ │ │ ├── Mask.java │ │ │ ├── PreComputedMask.java │ │ │ ├── StaticMask.java │ │ │ ├── operator/ │ │ │ │ ├── BinaryOperatorMask.java │ │ │ │ └── NotMask.java │ │ │ └── shape/ │ │ │ ├── CylinderMask.java │ │ │ └── SphereMask.java │ │ ├── selection/ │ │ │ ├── ISelection.java │ │ │ └── ISelectionManager.java │ │ └── utils/ │ │ ├── BetterBlockPos.java │ │ ├── BlockOptionalMeta.java │ │ ├── BlockOptionalMetaLookup.java │ │ ├── BlockUtils.java │ │ ├── BooleanBinaryOperator.java │ │ ├── BooleanBinaryOperators.java │ │ ├── Helper.java │ │ ├── IInputOverrideHandler.java │ │ ├── IPlayerContext.java │ │ ├── IPlayerController.java │ │ ├── MyChunkPos.java │ │ ├── NotificationHelper.java │ │ ├── Pair.java │ │ ├── PathCalculationResult.java │ │ ├── RayTraceUtils.java │ │ ├── Rotation.java │ │ ├── RotationUtils.java │ │ ├── SettingsUtil.java │ │ ├── TypeUtils.java │ │ ├── VecUtils.java │ │ ├── accessor/ │ │ │ └── IItemStack.java │ │ ├── gui/ │ │ │ └── BaritoneToast.java │ │ ├── input/ │ │ │ └── Input.java │ │ └── interfaces/ │ │ └── IGoalRenderPos.java │ ├── launch/ │ │ ├── java/ │ │ │ └── baritone/ │ │ │ └── launch/ │ │ │ ├── BaritoneMixinConnector.java │ │ │ └── mixins/ │ │ │ ├── MixinChunkArray.java │ │ │ ├── MixinClientChunkProvider.java │ │ │ ├── MixinClientPlayNetHandler.java │ │ │ ├── MixinClientPlayerEntity.java │ │ │ ├── MixinCommandSuggestionHelper.java │ │ │ ├── MixinEntity.java │ │ │ ├── MixinEntityRenderManager.java │ │ │ ├── MixinFireworkRocketEntity.java │ │ │ ├── MixinItemStack.java │ │ │ ├── MixinLivingEntity.java │ │ │ ├── MixinLootContext.java │ │ │ ├── MixinMinecraft.java │ │ │ ├── MixinNetworkManager.java │ │ │ ├── MixinPalettedContainer$Data.java │ │ │ ├── MixinPalettedContainer.java │ │ │ ├── MixinPlayerController.java │ │ │ ├── MixinScreen.java │ │ │ └── MixinWorldRenderer.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── MANIFEST.MF │ │ └── mixins.baritone.json │ ├── main/ │ │ └── java/ │ │ └── baritone/ │ │ ├── Baritone.java │ │ ├── BaritoneProvider.java │ │ ├── KeepName.java │ │ ├── behavior/ │ │ │ ├── Behavior.java │ │ │ ├── InventoryBehavior.java │ │ │ ├── LookBehavior.java │ │ │ ├── PathingBehavior.java │ │ │ ├── WaypointBehavior.java │ │ │ └── look/ │ │ │ └── ForkableRandom.java │ │ ├── cache/ │ │ │ ├── CachedChunk.java │ │ │ ├── CachedRegion.java │ │ │ ├── CachedWorld.java │ │ │ ├── ChunkPacker.java │ │ │ ├── FasterWorldScanner.java │ │ │ ├── WaypointCollection.java │ │ │ ├── WorldData.java │ │ │ ├── WorldProvider.java │ │ │ └── WorldScanner.java │ │ ├── command/ │ │ │ ├── CommandSystem.java │ │ │ ├── ExampleBaritoneControl.java │ │ │ ├── argparser/ │ │ │ │ ├── ArgParserManager.java │ │ │ │ └── DefaultArgParsers.java │ │ │ ├── argument/ │ │ │ │ ├── ArgConsumer.java │ │ │ │ ├── CommandArgument.java │ │ │ │ └── CommandArguments.java │ │ │ ├── defaults/ │ │ │ │ ├── AxisCommand.java │ │ │ │ ├── BlacklistCommand.java │ │ │ │ ├── BuildCommand.java │ │ │ │ ├── ClickCommand.java │ │ │ │ ├── ComeCommand.java │ │ │ │ ├── CommandAlias.java │ │ │ │ ├── DefaultCommands.java │ │ │ │ ├── ETACommand.java │ │ │ │ ├── ElytraCommand.java │ │ │ │ ├── ExecutionControlCommands.java │ │ │ │ ├── ExploreCommand.java │ │ │ │ ├── ExploreFilterCommand.java │ │ │ │ ├── FarmCommand.java │ │ │ │ ├── FindCommand.java │ │ │ │ ├── FollowCommand.java │ │ │ │ ├── ForceCancelCommand.java │ │ │ │ ├── GcCommand.java │ │ │ │ ├── GoalCommand.java │ │ │ │ ├── GotoCommand.java │ │ │ │ ├── HelpCommand.java │ │ │ │ ├── InvertCommand.java │ │ │ │ ├── LitematicaCommand.java │ │ │ │ ├── MineCommand.java │ │ │ │ ├── PathCommand.java │ │ │ │ ├── PickupCommand.java │ │ │ │ ├── ProcCommand.java │ │ │ │ ├── ReloadAllCommand.java │ │ │ │ ├── RenderCommand.java │ │ │ │ ├── RepackCommand.java │ │ │ │ ├── SaveAllCommand.java │ │ │ │ ├── SchematicaCommand.java │ │ │ │ ├── SelCommand.java │ │ │ │ ├── SetCommand.java │ │ │ │ ├── SurfaceCommand.java │ │ │ │ ├── ThisWayCommand.java │ │ │ │ ├── TunnelCommand.java │ │ │ │ ├── VersionCommand.java │ │ │ │ └── WaypointsCommand.java │ │ │ └── manager/ │ │ │ └── CommandManager.java │ │ ├── event/ │ │ │ └── GameEventHandler.java │ │ ├── pathing/ │ │ │ ├── calc/ │ │ │ │ ├── AStarPathFinder.java │ │ │ │ ├── AbstractNodeCostSearch.java │ │ │ │ ├── Path.java │ │ │ │ ├── PathNode.java │ │ │ │ └── openset/ │ │ │ │ ├── BinaryHeapOpenSet.java │ │ │ │ ├── IOpenSet.java │ │ │ │ └── LinkedListOpenSet.java │ │ │ ├── movement/ │ │ │ │ ├── CalculationContext.java │ │ │ │ ├── Movement.java │ │ │ │ ├── MovementHelper.java │ │ │ │ ├── MovementOption.java │ │ │ │ ├── MovementState.java │ │ │ │ ├── Moves.java │ │ │ │ └── movements/ │ │ │ │ ├── MovementAscend.java │ │ │ │ ├── MovementDescend.java │ │ │ │ ├── MovementDiagonal.java │ │ │ │ ├── MovementDownward.java │ │ │ │ ├── MovementFall.java │ │ │ │ ├── MovementParkour.java │ │ │ │ ├── MovementPillar.java │ │ │ │ └── MovementTraverse.java │ │ │ ├── path/ │ │ │ │ ├── CutoffPath.java │ │ │ │ ├── PathExecutor.java │ │ │ │ └── SplicedPath.java │ │ │ └── precompute/ │ │ │ ├── PrecomputedData.java │ │ │ └── Ternary.java │ │ ├── process/ │ │ │ ├── BackfillProcess.java │ │ │ ├── BuilderProcess.java │ │ │ ├── CustomGoalProcess.java │ │ │ ├── ElytraProcess.java │ │ │ ├── ExploreProcess.java │ │ │ ├── FarmProcess.java │ │ │ ├── FollowProcess.java │ │ │ ├── GetToBlockProcess.java │ │ │ ├── InventoryPauserProcess.java │ │ │ ├── MineProcess.java │ │ │ └── elytra/ │ │ │ ├── BlockStateOctreeInterface.java │ │ │ ├── ElytraBehavior.java │ │ │ ├── NetherPath.java │ │ │ ├── NetherPathfinderContext.java │ │ │ ├── NullElytraProcess.java │ │ │ ├── PathCalculationException.java │ │ │ └── UnpackedSegment.java │ │ ├── selection/ │ │ │ ├── Selection.java │ │ │ ├── SelectionManager.java │ │ │ └── SelectionRenderer.java │ │ └── utils/ │ │ ├── BaritoneMath.java │ │ ├── BaritoneProcessHelper.java │ │ ├── BlockBreakHelper.java │ │ ├── BlockPlaceHelper.java │ │ ├── BlockStateInterface.java │ │ ├── BlockStateInterfaceAccessWrapper.java │ │ ├── GuiClick.java │ │ ├── IRenderer.java │ │ ├── InputOverrideHandler.java │ │ ├── PathRenderer.java │ │ ├── PathingCommandContext.java │ │ ├── PathingControlManager.java │ │ ├── PlayerMovementInput.java │ │ ├── ToolSet.java │ │ ├── accessor/ │ │ │ ├── IChunkArray.java │ │ │ ├── IChunkProviderClient.java │ │ │ ├── IClientChunkProvider.java │ │ │ ├── IEntityRenderManager.java │ │ │ ├── IFireworkRocketEntity.java │ │ │ ├── IGuiScreen.java │ │ │ ├── IPalettedContainer.java │ │ │ └── IPlayerControllerMP.java │ │ ├── pathing/ │ │ │ ├── Avoidance.java │ │ │ ├── BetterWorldBorder.java │ │ │ ├── Favoring.java │ │ │ ├── MutableMoveResult.java │ │ │ ├── PathBase.java │ │ │ └── PathingBlockType.java │ │ ├── player/ │ │ │ ├── BaritonePlayerContext.java │ │ │ └── BaritonePlayerController.java │ │ ├── schematic/ │ │ │ ├── MapArtSchematic.java │ │ │ ├── SchematicSystem.java │ │ │ ├── SelectionSchematic.java │ │ │ ├── StaticSchematic.java │ │ │ ├── format/ │ │ │ │ ├── DefaultSchematicFormats.java │ │ │ │ └── defaults/ │ │ │ │ ├── LitematicaSchematic.java │ │ │ │ ├── MCEditSchematic.java │ │ │ │ └── SpongeSchematic.java │ │ │ ├── litematica/ │ │ │ │ └── LitematicaHelper.java │ │ │ └── schematica/ │ │ │ ├── SchematicAdapter.java │ │ │ └── SchematicaHelper.java │ │ └── type/ │ │ └── VarInt.java │ ├── schematica_api/ │ │ └── java/ │ │ ├── com/ │ │ │ └── github/ │ │ │ └── lunatrius/ │ │ │ ├── core/ │ │ │ │ └── util/ │ │ │ │ └── math/ │ │ │ │ └── MBlockPos.java │ │ │ └── schematica/ │ │ │ ├── Schematica.java │ │ │ ├── api/ │ │ │ │ └── ISchematic.java │ │ │ ├── client/ │ │ │ │ └── world/ │ │ │ │ └── SchematicWorld.java │ │ │ └── proxy/ │ │ │ ├── ClientProxy.java │ │ │ └── CommonProxy.java │ │ └── fi/ │ │ └── dy/ │ │ └── masa/ │ │ └── litematica/ │ │ ├── Litematica.java │ │ ├── data/ │ │ │ └── DataManager.java │ │ ├── schematic/ │ │ │ ├── LitematicaSchematic.java │ │ │ └── placement/ │ │ │ ├── SchematicPlacement.java │ │ │ ├── SchematicPlacementManager.java │ │ │ └── SubRegionPlacement.java │ │ └── world/ │ │ ├── SchematicWorldHandler.java │ │ └── WorldSchematic.java │ └── test/ │ └── java/ │ └── baritone/ │ ├── cache/ │ │ └── CachedRegionTest.java │ ├── pathing/ │ │ ├── calc/ │ │ │ └── openset/ │ │ │ └── OpenSetsTest.java │ │ ├── goals/ │ │ │ └── GoalGetToBlockTest.java │ │ └── movement/ │ │ └── ActionCostsTest.java │ └── utils/ │ └── pathing/ │ ├── BetterBlockPosTest.java │ └── PathingBlockTypeTest.java └── tweaker/ ├── build.gradle └── src/ └── main/ └── java/ └── baritone/ └── launch/ ├── LaunchTesting.java └── tweaker/ └── BaritoneTweaker.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ * text=auto ================================================ FILE: .github/ISSUE_TEMPLATE/bug.md ================================================ --- name: Bug report about: Please file a separate report for each issue title: Please add a brief but descriptive title labels: bug assignees: '' --- ## Some information Operating system: Java version: Minecraft version: Baritone version: Other mods (if used): ## Exception, error or logs Please find your `latest.log` or `debug.log` in this folder and attach it to the issue Linux: `~/.minecraft/logs/` Windows: `%appdata%/.minecraft/logs/` Mac: `/Library/Application\ Support/minecraft/logs/` ## How to reproduce Add your steps to reproduce the issue/bug experienced here. ## Modified settings To get the modified settings run `#modified` in game ## Final checklist - [x] I know how to properly use check boxes - [ ] I have included the version of Minecraft I'm running, baritone's version and forge mods (if used). - [ ] I have included logs, exceptions and / or steps to reproduce the issue. - [ ] I have not used any OwO's or UwU's in this issue. ================================================ FILE: .github/ISSUE_TEMPLATE/question.md ================================================ --- name: Question about: Please file a separate report for each question title: Please add a brief but descriptive title labels: question assignees: '' --- ## What do you need help with? With as much detail as possible, describe your question and what you may need help with. ## Final checklist - [x] I know how to properly use check boxes - [ ] I have not used any OwO's or UwU's in this issue. ================================================ FILE: .github/ISSUE_TEMPLATE/suggestion.md ================================================ --- name: Suggestion about: Please file a separate report for each suggestion title: Please add a brief but descriptive title labels: enhancement assignees: '' --- ## Describe your suggestion With as much detail as possible, describe what your suggestion would do for Baritone. ## Settings If applicable, what settings/customizability should be offered to tweak the functionality of your suggestion. ## Context Describe how your suggestion would improve Baritone, or the reason behind it being added. ## Final checklist - [x] I know how to properly use check boxes - [ ] I have not used any OwO's or UwU's in this issue. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ================================================ FILE: .github/workflows/gradle_build.yml ================================================ # This workflow will build a Java project with Gradle # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle name: Java CI with Gradle on: push: pull_request: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: gradle - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build -Pmod_version="$(git describe --always --tags --first-parent | cut -c2-)" - name: Archive Artifacts uses: actions/upload-artifact@v4 with: name: Artifacts path: dist/ - name: Archive mapping.txt uses: actions/upload-artifact@v4 with: name: Mappings path: mapping/ ================================================ FILE: .github/workflows/run_tests.yml ================================================ name: Tests on: push: pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Executing tests run: ./gradlew test ================================================ FILE: .gitignore ================================================ .DS_Store **/*.swp run/ autotest/ dist/ volderyarn/ # Gradle build/ .gradle/ classes/ *.class /out # IntelliJ Files .idea/ *.iml *.ipr *.iws /logs/ tweaker/logs/ common/logs/ # Eclipse Files .classpath .project .settings/ baritone_Client.launch # Copyright Files !/.idea/copyright/Baritone.xml !/.idea/copyright/profiles_settings.xml .vscode/launch.json .architectury-transformer mapping libs/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar libs/java-objc-bridge-1.1.jar mapping ================================================ FILE: .gitlab-ci.yml ================================================ image: java:8 before_script: - which java - which javac build: script: - ./gradlew build - ./gradlew build -Pbaritone.forge_build artifacts: paths: - dist/* expire_in: 1 week ================================================ FILE: .gitmessage ================================================ (<ticket>) # 📝 Update README.md (WD-1234) # ✅ Add unit test for inputs (WD-1234) # <emoji> can be: # 🎨 :art: when improving structure of the code # ⚡️ :zap: when improving performance # 🔥 :fire: when removing code or files # ✨ :sparkles: when introducing new features # 🚧 :construction: when work in progress # 🔨 :hammer: when refactoring code # 📝 :memo: when writing docs # 💄 :lipstick: when updating the UI and style files # 📈 :chart_with_upwards_trend: when adding analytics or tracking code # 🌐 :globe_with_meridians: when adding internationalization and localization # ✏️ :pencil2: when fixing typos # 🚚 :truck: when moving or renaming files # ✅ :white_check_mark: when adding tests # 👌 :ok_hand: when updating code due to code review changes # 🐛 :bug: when fixing a bug # 🚑 :ambulance: when doing a critical hotfix # 🚨 :rotating_light: when removing linter warnings # 🔀 :twisted_rightwards_arrows: when merging branches # ⬆️ :arrow_up: when upgrading dependencies # ⬇️ :arrow_down: when downgrading dependencies # 🔧 :wrench: when changing configuration files # 🔖 :bookmark: when releasing / version tagging # 💚 :green_heart: when fixing the CI build ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * No Anime (including uwu's or owo's) * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Giving and gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * Anime * The use of sexualized language or imagery and unwelcome sexual attention or advances * ~~Trolling, insulting/derogatory comments, and personal or political attacks~~ * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission or consent * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. Project maintainers have the right and responsibility to immediately remove without any sort of dispute any issues or pull requests that do not align with their corresponding templates. Absolutely no leniancy shall be accepted with these terms. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at complaints@leijurv.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: Dockerfile ================================================ FROM ubuntu:focal ENV DEBIAN_FRONTEND noninteractive RUN apt update -y RUN apt install \ openjdk-17-jdk \ git \ --assume-yes COPY . /code WORKDIR /code RUN ./gradlew build ================================================ FILE: FEATURES.md ================================================ # Pathing features - **Long distance pathing and splicing** Baritone calculates paths in segments, and precalculates the next segment when the current one is about to end, so that it's moving towards the goal at all times. - **Chunk caching** Baritone simplifies chunks to a compacted internal 2-bit representation (AIR, SOLID, WATER, AVOID) and stores them in RAM for better very-long-distance pathing. There is also an option to save these cached chunks to disk. <a href="https://www.youtube.com/watch?v=dyfYKSubhdc">Example</a> - **Block breaking** Baritone considers breaking blocks as part of its path. It also takes into account your current tool set and hot bar. For example, if you have a Eff V diamond pick, it may choose to mine through a stone barrier, while if you only had a wood pick it might be faster to climb over it. - **Block placing** Baritone considers placing blocks as part of its path. This includes sneak-back-placing, pillaring, etc. It has a configurable penalty of placing a block (set to 1 second by default), to conserve its resources. The list of acceptable throwaway blocks is also configurable, and is cobble, dirt, or netherrack by default. <a href="https://www.youtube.com/watch?v=F6FbI1L9UmU">Example</a> - **Falling** Baritone will fall up to 3 blocks onto solid ground (configurable, if you have Feather Falling and/or don't mind taking a little damage). If you have a water bucket on your hotbar, it will fall up to 23 blocks and place the bucket beneath it. It will fall an unlimited distance into existing still water. - **Vines and ladders** Baritone understands how to climb and descend vines and ladders. There is experimental support for more advanced maneuvers, like strafing to a different ladder / vine column in midair (off by default, setting named `allowVines`). Baritone can break its fall by grabbing ladders / vines midair, and understands when that is and isn't possible. - **Opening fence gates and doors** - **Slabs and stairs** - **Falling blocks** Baritone understands the costs of breaking blocks with falling blocks on top, and includes all of their break costs. Additionally, since it avoids breaking any blocks touching a liquid, it won't break the bottom of a gravel stack below a lava lake (anymore). - **Avoiding dangerous blocks** Obviously, it knows not to walk through fire or on magma, not to corner over lava (that deals some damage), not to break any blocks touching a liquid (it might drown), etc. - **Parkour** Sprint jumping over 1, 2, or 3 block gaps - **Parkour place** Sprint jumping over a 3 block gap and placing the block to land on while executing the jump. It's really cool. - **Pigs** It can sort of control pigs. I wouldn't rely on it though. # Pathing method Baritone uses A*, with some modifications: - **Segmented calculation** Traditional A* calculates until the most promising node is in the goal, however in the environment of Minecraft with a limited render distance, we don't know the environment all the way to our goal. Baritone has three possible ways for path calculation to end: finding a path all the way to the goal, running out of time, or getting to the render distance. In the latter two scenarios, the selection of which segment to actually execute falls to the next item (incremental cost backoff). Whenever the path calculation thread finds that the best / most promising node is at the edge of loaded chunks, it increments a counter. If this happens more than 50 times (configurable), path calculation exits early. This happens with very low render distances. Otherwise, calculation continues until the timeout is hit (also configurable) or we find a path all the way to the goal. - **Incremental cost backoff** When path calculation exits early without getting all the way to the goal, Baritone it needs to select a segment to execute first (assuming it will calculate the next segment at the end of this one). It uses incremental cost backoff to select the best node by varying metrics, then paths to that node. This is unchanged from MineBot and I made a <a href="https://docs.google.com/document/d/1WVHHXKXFdCR1Oz__KtK8sFqyvSwJN_H4lftkHFgmzlc/edit">write-up</a> that still applies. In essence, it keeps track of the best node by various increasing coefficients, then picks the node with the least coefficient that goes at least 5 blocks from the starting position. - **Minimum improvement repropagation** The pathfinder ignores alternate routes that provide minimal improvements (less than 0.01 ticks of improvement), because the calculation cost of repropagating this to all connected nodes is much higher than the half-millisecond path time improvement it would get. - **Backtrack cost favoring** While calculating the next segment, Baritone favors backtracking its current segment. The cost is decreased heavily, but is still positive (this won't cause it to backtrack if it doesn't need to). This allows it to splice and jump onto the next segment as early as possible, if the next segment begins with a backtrack of the current one. <a href="https://www.youtube.com/watch?v=CGiMcb8-99Y">Example</a> - **Backtrack detection and pausing** While path calculation happens on a separate thread, the main game thread has access to the latest node considered, and the best path so far (those are rendered light blue and dark blue respectively). When the current best path (rendered dark blue) passes through the player's current position on the current path segment, path execution is paused (if it's safe to do so), because there's no point continuing forward if we're about to turn around and go back that same way. Note that the current best path as reported by the path calculation thread takes into account the incremental cost backoff system, so it's accurate to what the path calculation thread will actually pick once it finishes. # Chat control - [Baritone chat control usage](USAGE.md) # Goals The pathing goal can be set to any of these options: - **GoalBlock** one specific block that the player should stand inside at foot level - **GoalXZ** an X and a Z coordinate, used for long distance pathing - **GoalYLevel** a Y coordinate - **GoalTwoBlocks** a block position that the player should stand in, either at foot or eye level - **GoalGetToBlock** a block position that the player should stand adjacent to, below, or on top of - **GoalNear** a block position that the player should get within a certain radius of, used for following entities - **GoalAxis** a block position on an axis or diagonal axis (so x=0, z=0, or x=z), and y=120 (configurable) And finally `GoalComposite`. `GoalComposite` is a list of other goals, any one of which satisfies the goal. For example, `mine diamond_ore` creates a `GoalComposite` of `GoalTwoBlocks`s for every diamond ore location it knows of. # Future features Things it doesn't have yet - Trapdoors - Sprint jumping in a 1x2 corridor See <a href="https://github.com/cabaletta/baritone/issues">issues</a> for more. Things it may not ever have, from most likely to least likely =( - Boats - Horses (2x3 path instead of 1x2) ================================================ FILE: LICENSE ================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ================================================ FILE: README.md ================================================ # Baritone <p align="center"> <a href="https://github.com/cabaletta/baritone/releases/"><img src="https://img.shields.io/github/downloads/cabaletta/baritone/total.svg" alt="GitHub All Releases"/></a> </p> <p align="center"> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.12.2-brightgreen.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.13.2-yellow.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.14.4-yellow.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.15.2-yellow.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.16.5-yellow.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.17.1-yellow.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.18.2-yellow.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.19.2-brightgreen.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.19.4-brightgreen.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.20.1-brightgreen.svg" alt="Minecraft"/></a> <a href="#Baritone"><img src="https://img.shields.io/badge/MC-1.21.3-brightgreen.svg" alt="Minecraft"/></a> </p> <p align="center"> <a href="https://travis-ci.com/cabaletta/baritone/"><img src="https://travis-ci.com/cabaletta/baritone.svg?branch=master" alt="Build Status"/></a> <a href="https://github.com/cabaletta/baritone/releases/"><img src="https://img.shields.io/github/release/cabaletta/baritone.svg" alt="Release"/></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-LGPL--3.0%20with%20anime%20exception-green.svg" alt="License"/></a> <a href="https://www.codacy.com/gh/cabaletta/baritone/dashboard?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade"><img src="https://app.codacy.com/project/badge/Grade/cadab857dab049438b6e28b3cfc5570e" alt="Codacy Badge"/></a> <a href="https://github.com/cabaletta/baritone/blob/master/CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-blue.svg?style=flat" alt="Code of Conduct"/></a> <a href="https://snyk.io/test/github/cabaletta/baritone?targetFile=build.gradle"><img src="https://snyk.io/test/github/cabaletta/baritone/badge.svg?targetFile=build.gradle" alt="Known Vulnerabilities"/></a> <a href="https://github.com/cabaletta/baritone/issues/"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="Contributions welcome"/></a> <a href="https://github.com/cabaletta/baritone/issues/"><img src="https://img.shields.io/github/issues/cabaletta/baritone.svg" alt="Issues"/></a> <a href="https://github.com/cabaletta/baritone/issues?q=is%3Aissue+is%3Aclosed"><img src="https://img.shields.io/github/issues-closed/cabaletta/baritone.svg" alt="GitHub issues-closed"/></a> <a href="https://github.com/cabaletta/baritone/pulls/"><img src="https://img.shields.io/github/issues-pr/cabaletta/baritone.svg" alt="Pull Requests"/></a> <a href="https://github.com/cabaletta/baritone/graphs/contributors/"><img src="https://img.shields.io/github/contributors/cabaletta/baritone.svg" alt="GitHub contributors"/></a> <a href="https://github.com/cabaletta/baritone/commit/"><img src="https://img.shields.io/github/commits-since/cabaletta/baritone/v1.0.0.svg" alt="GitHub commits"/></a> <img src="https://img.shields.io/github/languages/code-size/cabaletta/baritone.svg" alt="Code size"/> <img src="https://img.shields.io/github/repo-size/cabaletta/baritone.svg" alt="GitHub repo size"/> <img src="https://tokei.rs/b1/github/cabaletta/baritone?category=code&style=flat" alt="Lines of Code"/> <img src="https://img.shields.io/badge/Badges-36-blue.svg" alt="yes"/> </p> <p align="center"> <a href="https://impactclient.net/"><img src="https://img.shields.io/badge/Impact%20integration-v1.2.14%20/%20v1.3.8%20/%20v1.4.6%20/%20v1.5.3%20/%20v1.6.3-brightgreen.svg" alt="Impact integration"/></a> <a href="https://github.com/lambda-client/lambda"><img src="https://img.shields.io/badge/Lambda%20integration-v1.2.17-brightgreen.svg" alt="Lambda integration"/></a> <a href="https://github.com/fr1kin/ForgeHax/"><img src="https://img.shields.io/badge/ForgeHax%20%22integration%22-scuffed-yellow.svg" alt="ForgeHax integration"/></a> <a href="https://aristois.net/"><img src="https://img.shields.io/badge/Aristois%20add--on%20integration-v1.6.3-green.svg" alt="Aristois add-on integration"/></a> <a href="https://rootnet.dev/"><img src="https://img.shields.io/badge/rootNET%20integration-v1.2.14-green.svg" alt="rootNET integration"/></a> <a href="https://futureclient.net/"><img src="https://img.shields.io/badge/Future%20integration-v1.2.12%20%2F%20v1.3.6%20%2F%20v1.4.4-red" alt="Future integration"/></a> <a href="https://rusherhack.org/"><img src="https://img.shields.io/badge/RusherHack%20integration-v1.2.14-green" alt="RusherHack integration"/></a> </p> <p align="center"> <a href="http://forthebadge.com/"><img src="https://web.archive.org/web/20230604002050/https://forthebadge.com/images/badges/built-with-swag.svg" alt="forthebadge"/></a> <a href="http://forthebadge.com/"><img src="https://web.archive.org/web/20230604002050/https://forthebadge.com/images/badges/mom-made-pizza-rolls.svg" alt="forthebadge"/></a> </p> A Minecraft pathfinder bot. Baritone is the pathfinding system used in [Impact](https://impactclient.net/) since 4.4. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a (very old!) video I made showing off what it can do. [**Baritone Discord Server**](http://discord.gg/s6fRBAUpmr) **Quick download links:** | Forge | Fabric | NeoForge | |---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| | [1.12.2 Forge](https://github.com/cabaletta/baritone/releases/download/v1.2.19/baritone-api-forge-1.2.19.jar) | | | | [1.16.5 Forge](https://github.com/cabaletta/baritone/releases/download/v1.6.5/baritone-api-forge-1.6.5.jar) | [1.16.5 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.6.5/baritone-api-fabric-1.6.5.jar) | | | [1.17.1 Forge](https://github.com/cabaletta/baritone/releases/download/v1.7.3/baritone-api-forge-1.7.3.jar) | [1.17.1 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.7.3/baritone-api-fabric-1.7.3.jar) | | | [1.18.2 Forge](https://github.com/cabaletta/baritone/releases/download/v1.8.6/baritone-api-forge-1.8.6.jar) | [1.18.2 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.8.6/baritone-api-fabric-1.8.6.jar) | | | [1.19.2 Forge](https://github.com/cabaletta/baritone/releases/download/v1.9.4/baritone-api-forge-1.9.4.jar) | [1.19.2 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.9.4/baritone-api-fabric-1.9.4.jar) | | | [1.19.3 Forge](https://github.com/cabaletta/baritone/releases/download/v1.9.1/baritone-api-forge-1.9.1.jar) | [1.19.3 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.9.1/baritone-api-fabric-1.9.1.jar) | | | [1.19.4 Forge](https://github.com/cabaletta/baritone/releases/download/v1.9.5/baritone-api-forge-1.9.5.jar) | [1.19.4 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.9.5/baritone-api-fabric-1.9.5.jar) | | | [1.20.1 Forge](https://github.com/cabaletta/baritone/releases/download/v1.10.3/baritone-api-forge-1.10.3.jar) | [1.20.1 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.10.3/baritone-api-fabric-1.10.3.jar) | | | [1.20.3 Forge](https://github.com/cabaletta/baritone/releases/download/v1.10.4/baritone-api-forge-1.10.4.jar) | [1.20.3 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.10.4/baritone-api-fabric-1.10.4.jar) | [1.20.3 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.10.4/baritone-api-neoforge-1.10.4.jar) | | [1.20.4 Forge](https://github.com/cabaletta/baritone/releases/download/v1.10.4/baritone-api-forge-1.10.4.jar) | [1.20.4 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.10.4/baritone-api-fabric-1.10.4.jar) | [1.20.4 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.10.4/baritone-api-neoforge-1.10.4.jar) | | [1.21.1 Forge](https://github.com/cabaletta/baritone/releases/download/v1.11.2/baritone-api-forge-1.11.2.jar) | [1.21.1 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.11.2/baritone-api-fabric-1.11.2.jar) | [1.21.1 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.11.2/baritone-api-neoforge-1.11.2.jar) | | [1.21.3 Forge](https://github.com/cabaletta/baritone/releases/download/v1.11.1/baritone-api-forge-1.11.1.jar) | [1.21.3 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.11.1/baritone-api-fabric-1.11.1.jar) | [1.21.3 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.11.1/baritone-api-neoforge-1.11.1.jar) | | [1.21.4 Forge](https://github.com/cabaletta/baritone/releases/download/v1.13.1/baritone-api-forge-1.13.1.jar) | [1.21.4 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.13.1/baritone-api-fabric-1.13.1.jar) | [1.21.4 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.13.1/baritone-api-neoforge-1.13.1.jar) | | [1.21.5 Forge](https://github.com/cabaletta/baritone/releases/download/v1.14.0/baritone-api-forge-1.14.0.jar) | [1.21.5 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.14.0/baritone-api-fabric-1.14.0.jar) | [1.21.5 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.14.0/baritone-api-neoforge-1.14.0.jar) | | [1.21.6 Forge](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-forge-1.15.0.jar) | [1.21.6 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-fabric-1.15.0.jar) | [1.21.6 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-neoforge-1.15.0.jar) | | [1.21.7 Forge](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-forge-1.15.0.jar) | [1.21.7 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-fabric-1.15.0.jar) | [1.21.7 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-neoforge-1.15.0.jar) | | [1.21.8 Forge](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-forge-1.15.0.jar) | [1.21.8 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-fabric-1.15.0.jar) | [1.21.8 NeoForge](https://github.com/cabaletta/baritone/releases/download/v1.15.0/baritone-api-neoforge-1.15.0.jar) | **How to immediately get started:** Type `#goto 1000 500` in chat to go to x=1000 z=500. Type `#mine diamond_ore` to mine diamond ore. Type `#stop` to stop. For more, read [the usage page](USAGE.md) and/or watch this [tutorial playlist](https://www.youtube.com/playlist?list=PLnwnJ1qsS7CoQl9Si-RTluuzCo_4Oulpa). Also try `#elytra` for Elytra flying in the Nether using fireworks ([trailer](https://youtu.be/4bGGPo8yiHo), [usage](https://youtu.be/NnSlQi-68eQ)). For help, join the [Baritone Discord Server](http://discord.gg/s6fRBAUpmr). For other versions of Minecraft or more complicated situations or for development, see [Installation & setup](SETUP.md). For 1.16.5, [click here](https://www.youtube.com/watch?v=_4eVJ9Qz2J8) and see description. Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it. There's a [showcase video](https://youtu.be/CZkLXWo4Fg4) made by @Adovin#6313 on Baritone which I recommend. For help, join the [Baritone Discord Server](http://discord.gg/s6fRBAUpmr). This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/), the original version of the bot for Minecraft 1.8.9, rebuilt for 1.12.2 onwards. Baritone focuses on reliability and particularly performance (it's over [30x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) than MineBot at calculating paths). Have committed at least once a day from Aug 1, 2018, to Aug 1, 2019. 1Leijurv3DWTrGAfmmiTphjhXLvQiHg7K2 # Getting Started Here are some links to help to get started: - [Features](FEATURES.md) - [Installation & setup](SETUP.md) - [API Javadocs](https://baritone.leijurv.com/) - [Settings](https://baritone.leijurv.com/baritone/api/Settings.html#field.detail) - [Usage (chat control)](USAGE.md) ## Stars over time [![Stargazers over time](https://starchart.cc/cabaletta/baritone.svg)](https://starchart.cc/cabaletta/baritone) # API The API is heavily documented, you can find the Javadocs for the latest release [here](https://baritone.leijurv.com/). Please note that usage of anything located outside of the ``baritone.api`` package is not supported by the API release jar. Below is an example of basic usage for changing some settings, and then pathing to an X/Z goal. ```java BaritoneAPI.getSettings().allowSprint.value = true; BaritoneAPI.getSettings().primaryTimeoutMS.value = 2000L; BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalXZ(10000, 20000)); ``` # FAQ ## Can I use Baritone as a library in my custom utility client? That's what it's for, sure! (As long as usage complies with the LGPL 3.0 License) ## How is it so fast? Magic. (Hours of [leijurv](https://github.com/leijurv/) enduring excruciating pain) ### Additional Special Thanks To: ![YourKit-Logo](https://www.yourkit.com/images/yklogo.png) YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of the [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/). We thank them for granting Baritone an OSS license so that we can make our software the best it can be. ## Why is it called Baritone? It's named for FitMC's deep sultry voice. ================================================ FILE: SETUP.md ================================================ # Installation The easiest way to install Baritone is to install it as Forge/Neoforge/Fabric mod, but if you know how you can also use it with a custom `version.json` (Examples: [1.14.4](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1), [1.15.2](https://www.dropbox.com/s/8rx6f0kts9hvd4f/1.15.2-Baritone.zip?dl=1), [1.16.5](https://www.dropbox.com/s/i6f292o2i7o9acp/1.16.5-Baritone.zip?dl=1)). Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it. ## Prebuilt official releases Releases are made rarely and are not always up to date with the latest features and bug fixes. Link to the releases page: [Releases](https://github.com/cabaletta/baritone/releases) The mapping between Minecraft versions and major Baritone versions is as follows | Minecraft version | 1.12 | 1.13 | 1.14 | 1.15 | 1.16 | 1.17 | 1.18 | 1.19 | 1.20 | 1.21 | 1.21.4 | 1.21.5 | 1.21.6 - 1.21.8 | |-------------------|------|------|------|------|------|------|------|------|-------|-------|--------|--------|------------------| | Baritone version | v1.2 | v1.3 | v1.4 | v1.5 | v1.6 | v1.7 | v1.8 | v1.9 | v1.10 | v1.11 | v1.13 | v1.14 | v1.15 | Any official release will be GPG signed by leijurv (44A3EA646EADAC6A). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by that public keys of `checksums.txt`. The build is fully deterministic and reproducible, and you can verify that by running `docker build --no-cache -t cabaletta/baritone .` yourself and comparing the shasum. This works identically on Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too). ## Artifacts Building Baritone will create the final artifacts in the ``dist`` directory. These are the same as the artifacts created in the [releases](https://github.com/cabaletta/baritone/releases). **The Forge, NeoForge and Fabric releases can simply be added as a Forge/Neoforge/Fabric mods.** If another one of your other mods has a Baritone integration, you want `baritone-api-*-VERSION.jar`. If you want to report a bug and spare us some effort, you want `baritone-unoptimized-*-VERSION.jar`. Otherwise, you want `baritone-standalone-*-VERSION.jar` Here's what the various qualifiers mean - **API**: Only the non-api packages are obfuscated. This should be used in environments where other mods would like to use Baritone's features. - **Standalone**: Everything is obfuscated. Other mods cannot use Baritone, but you get a bit of extra performance. - **Unoptimized**: Nothing is obfuscated. This shouldn't be used in production, but is really helpful for crash reports. - **No loader**: Loadable as a launchwrapper tweaker against vanilla Minecraft using a custom `version.json`. - **Forge/Neoforge/Fabric**: Loadable as a standard mod using the respective loader. The fabric build may or may not work on Quilt. If you build from source you will also find mapping files in the `dist` directory. These contain the renamings done by ProGuard and are useful if you want to read obfuscated stack traces. ## Build it yourself - Clone or download Baritone ![Image](https://i.imgur.com/kbqBtoN.png) - If you choose to download, make sure you download the correct branch and extract the ZIP archive. - Follow one of the instruction sets below, based on your preference ## Command Line On Mac OSX and Linux, use `./gradlew` instead of `gradlew`. The recommended Java versions by Minecraft version are | Minecraft version | Java version | |-------------------------------|---------------| | 1.12.2 - 1.16.5 | 8 | | 1.17.1 | 16 | | 1.18.2 - 1.20.4 | 17 | | 1.20.5 - 1.21.8 | 21 | Download java: https://adoptium.net/ To check which java version you are using do `java -version` in a command prompt or terminal. ### Building Baritone These tasks depend on the minecraft version, but are (for the most part) standard for building mods. For more details, see [the build ci action](/.github/workflows/gradle_build.yml) of the branch you want to build. For most branches `gradlew build` should build everything, but there are exceptions and this file might be out of date. More specifically, on older branches the setup used to be that `gradlew build` builds the tweaker jar and `gradlew build -Pbaritone.forge_build` / `gradlew build -Pbaritone.fabric_build` are needed to build for Forge/Fabric instead. And you might have to run `setupDecompWorkspace` first. ## IntelliJ - Open the project in IntelliJ as a Gradle project - Refresh the Gradle project (or, to be safe, just restart IntelliJ) - Depending on the minecraft version, you may need to run `setupDecompWorkspace` or `genIntellijRuns` in order to get everything working ## Github Actions Most branches have a CI workflow at `.github/workflows/gradle_build.yml`. If you fork this repository and enable actions for your fork you can push a dummy commit to trigger it and have GitHub build Baritone for you. If the commit you want to build is less than 90 days old, you can also find the corresponding workflow run in [this list](https://github.com/cabaletta/baritone/actions/workflows/gradle_build.yml) and download the artifacts from there. ================================================ FILE: USAGE.md ================================================ (assuming you already have Baritone [set up](SETUP.md)) # Prefix Baritone's chat control prefix is `#` by default. In Impact, you can also use `.b` as a prefix. (for example, `.b click` instead of `#click`) Baritone commands can also by default be typed in the chatbox. However if you make a typo, like typing "gola 10000 10000" instead of "goal" it goes into public chat, which is bad, so using `#` is suggested. To disable direct chat control (with no prefix), turn off the `chatControl` setting. To disable chat control with the `#` prefix, turn off the `prefixControl` setting. In Impact, `.b` cannot be disabled. Be careful that you don't leave yourself with all control methods disabled (if you do, reset your settings by deleting the file `minecraft/baritone/settings.txt` and relaunching). # For Baritone 1.2.10+, 1.3.5+, 1.4.2+ Lots of the commands have changed, BUT `#help` is improved vastly (its clickable! commands have tab completion! oh my!). Try `#help` I promise it won't just send you back here =) "wtf where is cleararea" -> look at `#help sel` "wtf where is goto death, goto waypoint" -> look at `#help wp` just look at `#help` lmao Watch this [showcase video](https://youtu.be/CZkLXWo4Fg4)! # Commands [Tutorial playlist](https://www.youtube.com/playlist?list=PLnwnJ1qsS7CoQl9Si-RTluuzCo_4Oulpa) **All** of these commands may need a prefix before them, as above ^. `help` To toggle a boolean setting, just say its name in chat (for example saying `allowBreak` toggles whether Baritone will consider breaking blocks). For a numeric setting, say its name then the new value (like `primaryTimeoutMS 250`). It's case insensitive. To reset a setting to its default value, say `acceptableThrowawayItems reset`. To reset all settings, say `reset`. To see all settings that have been modified from their default values, say `modified`. Commands in Baritone: - `thisway 1000` then `path` to go in the direction you're facing for a thousand blocks - `goal x y z` or `goal x z` or `goal y`, then `path` to set a goal to a certain coordinate then path to it - `goto x y z` or `goto x z` or `goto y` to go to a certain coordinate (in a single step, starts going immediately) - `goal` to set the goal to your player's feet - `goal clear` to clear the goal - `cancel` or `stop` to stop everything, `forcecancel` is also an option - `goto portal` or `goto ender_chest` or `goto block_type` to go to a block. (in Impact, `.goto` is an alias for `.b goto` for the most part) - `mine diamond_ore iron_ore` to mine diamond ore or iron ore (turn on the setting `legitMine` to only mine ores that it can actually see. It will explore randomly around y=11 until it finds them.) An amount of blocks can also be specified, for example, `mine 64 diamond_ore`. - `click` to click your destination on the screen. Right click path to on top of the block, left click to path into it (either at foot level or eye level), and left click and drag to select an area (`#help sel` to see what you can do with that selection). - `follow player playerName` to follow a player. `follow players` to follow any players in range (combine with Kill Aura for a fun time). `follow entities` to follow any entities. `follow entity pig` to follow entities of a specific type. - `wp` for waypoints. A "tag" is like "home" (created automatically on right clicking a bed) or "death" (created automatically on death) or "user" (has to be created manually). So you might want `#wp save user coolbiome`, then to set the goal `#wp goal coolbiome` then `#path` to path to it. For death, `#wp goal death` will list waypoints under the "death" tag (remember stuff is clickable!) - `build` to build a schematic. `build blah.schematic` will load `schematics/blah.schematic` and build it with the origin being your player feet. `build blah.schematic x y z` to set the origin. Any of those can be relative to your player (`~ 69 ~-420` would build at x=player x, y=69, z=player z-420). - `schematica` to build the schematic that is currently open in schematica - `tunnel` to dig and make a tunnel, 1x2. It will only deviate from the straight line if necessary such as to avoid lava. For a dumber tunnel that is really just cleararea, you can `tunnel 3 2 100`, to clear an area 3 high, 2 wide, and 100 deep. - `farm` to automatically harvest, replant, or bone meal crops. Use `farm <range>` or `farm <range> <waypoint>` to limit the max distance from the starting point or a waypoint. - `axis` to go to an axis or diagonal axis at y=120 (`axisHeight` is a configurable setting, defaults to 120). - `explore x z` to explore the world from the origin of x,z. Leave out x and z to default to player feet. This will continually path towards the closest chunk to the origin that it's never seen before. `explorefilter filter.json` with optional invert can be used to load in a list of chunks to load. - `invert` to invert the current goal and path. This gets as far away from it as possible, instead of as close as possible. For example, do `goal` then `invert` to run as far as possible from where you're standing at the start. - `come` tells Baritone to head towards your camera, useful when freecam doesn't move your player position. - `blacklist` will stop baritone from going to the closest block so it won't attempt to get to it. - `eta` to get information about the estimated time until the next segment and the goal, be aware that the ETA to your goal is really unprecise. - `proc` to view miscellaneous information about the process currently controlling Baritone. - `repack` to re-cache the chunks around you. - `gc` to call `System.gc()` which may free up some memory. - `render` to fix glitched chunk rendering without having to reload all of them. - `reloadall` to reload Baritone's world cache or `saveall` to save Baritone's world cache. - `find` to search through Baritone's cache and attempt to find the location of the block. - `surface` or `top` to tell Baritone to head towards the closest surface-like area, this can be the surface or highest available air space. - `version` to get the version of Baritone you're running - `damn` daniel All the settings and documentation are <a href="https://github.com/cabaletta/baritone/blob/master/src/api/java/baritone/api/Settings.java">here</a>. If you find HTML easier to read than Javadoc, you can look <a href="https://baritone.leijurv.com/baritone/api/Settings.html#field.detail">here</a>. There are about a hundred settings, but here are some fun / interesting / important ones that you might want to look at changing in normal usage of Baritone. The documentation for each can be found at the above links. - `allowBreak` - `allowSprint` - `allowPlace` - `allowParkour` - `allowParkourPlace` - `blockPlacementPenalty` - `renderCachedChunks` (and `cachedChunksOpacity`) <-- very fun but you need a beefy computer - `avoidance` (avoidance of mobs / mob spawners) - `legitMine` - `followRadius` - `backfill` (fill in tunnels behind you) - `buildInLayers` - `buildRepeatDistance` and `buildRepeatDirection` - `worldExploringChunkOffset` - `acceptableThrowawayItems` - `blocksToAvoidBreaking` - `mineScanDroppedItems` - `allowDiagonalAscend` # Troubleshooting / common issues ## Why doesn't Baritone respond to any of my chat commands? This could be one of many things. First, make sure it's actually installed. An easy way to check is seeing if it created the folder `baritone` in your Minecraft folder. Second, make sure that you're using the prefix properly, and that chat control is enabled in the way you expect. For example, Impact disables direct chat control. (i.e. anything typed in chat without a prefix will be ignored and sent publicly). **This is a saved setting**, so if you run Impact once, `chatControl` will be off from then on, **even in other clients**. So you'll need to use the `#` prefix or edit `baritone/settings.txt` in your Minecraft folder to undo that (specifically, remove the line `chatControl false` then restart your client). ## Why can I do `.goto x z` in Impact but nowhere else? Why can I do `-path to x z` in KAMI but nowhere else? These are custom commands that they added; those aren't from Baritone. The equivalent you're looking for is `goto x z`. ================================================ FILE: build.gradle ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ allprojects { apply plugin: 'java' apply plugin: "xyz.wagyourtail.unimined" apply plugin: "maven-publish" archivesBaseName = rootProject.archives_base_name def vers = "" try { vers = 'git describe --always --tags --first-parent --dirty'.execute().text.trim() } catch (Exception e) { println "Version detection failed: " + e } if (!vers.startsWith("v")) { println "using version number: " + rootProject.mod_version version = rootProject.mod_version } else { version = vers.substring(1) println "Detected version " + version } group = rootProject.maven_group sourceCompatibility = targetCompatibility = JavaVersion.toVersion(project.java_version) java { toolchain { languageVersion.set(JavaLanguageVersion.of(sourceCompatibility.majorVersion.toInteger())) } } repositories { maven { name = 'spongepowered-repo' url = 'https://repo.spongepowered.org/repository/maven-public/' } maven { name = 'fabric-maven' url = 'https://maven.fabricmc.net/' } maven { name = 'impactdevelopment-repo' url = 'https://impactdevelopment.github.io/maven/' } maven { name = "ldtteam" url = "https://maven.parchmentmc.net/" } // for the newer version of launchwrapper maven { name = "multimc-maven" url = "https://files.multimc.org/maven/" metadataSources { artifact() } } mavenCentral() maven { name = 'babbaj-repo' url = 'https://babbaj.github.io/maven/' } } dependencies { compileOnly "org.spongepowered:mixin:${project.mixin_version}" compileOnly "org.ow2.asm:asm:${project.asm_version}" implementation "dev.babbaj:nether-pathfinder:${project.nether_pathfinder_version}" } unimined.minecraft(sourceSets.main, true) { version rootProject.minecraft_version mappings { intermediary() mojmap() parchment("2023.06.26") } } tasks.withType(JavaCompile).configureEach { it.options.encoding = "UTF-8" def targetVersion = project.java_version.toInteger() if (JavaVersion.current().isJava9Compatible()) { it.options.release = targetVersion } } } unimined.minecraft { runs.off = true defaultRemapJar = false } archivesBaseName = archivesBaseName + "-common" sourceSets { api { compileClasspath += main.compileClasspath runtimeClasspath += main.runtimeClasspath } main { compileClasspath += api.output runtimeClasspath += api.output } test { compileClasspath += main.compileClasspath + main.runtimeClasspath + main.output runtimeClasspath += main.compileClasspath + main.runtimeClasspath + main.output } launch { compileClasspath += main.compileClasspath + main.runtimeClasspath + main.output runtimeClasspath += main.compileClasspath + main.runtimeClasspath + main.output } schematica_api { compileClasspath += main.compileClasspath runtimeClasspath += main.runtimeClasspath } main { compileClasspath += schematica_api.output runtimeClasspath += schematica_api.output } } dependencies { testImplementation 'junit:junit:4.13.2' } jar { from sourceSets.main.output, sourceSets.launch.output, sourceSets.api.output } javadoc { options.addStringOption('Xwerror', '-quiet') // makes the build fail on travis when there is a javadoc error options.linkSource true options.encoding "UTF-8" // allow emoji in comments :^) source = sourceSets.api.allJava classpath += sourceSets.api.compileClasspath } ================================================ FILE: buildSrc/.gitignore ================================================ build/ .gradle/ out/ ================================================ FILE: buildSrc/build.gradle ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ repositories { mavenLocal() maven { name = 'WagYourMaven' url = 'https://maven.wagyourtail.xyz/releases' } maven { name = 'ForgeMaven' url = 'https://maven.minecraftforge.net/' } maven { name = 'FabricMaven' url = 'https://maven.fabricmc.net/' } maven { name = 'NeoForgedMaven' url = 'https://maven.neoforged.net/' } mavenCentral() } dependencies { implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0' implementation group: 'commons-io', name: 'commons-io', version: '2.7' implementation group: 'xyz.wagyourtail.unimined', name: 'xyz.wagyourtail.unimined.gradle.plugin', version: '1.0.5' } ================================================ FILE: buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.gradle.task; import org.gradle.api.DefaultTask; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.TaskAction; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * @author Brady * @since 10/12/2018 */ class BaritoneGradleTask extends DefaultTask { protected static final String PROGUARD_ZIP = "proguard-%s.zip", PROGUARD_JAR = "proguard-%s.jar", PROGUARD_CONFIG_TEMPLATE = "scripts/proguard.pro", PROGUARD_CONFIG_DEST = "template.pro", PROGUARD_API_CONFIG = "api.pro", PROGUARD_STANDALONE_CONFIG = "standalone.pro", PROGUARD_EXPORT_PATH = "proguard_out.jar", PROGUARD_MAPPING_DIR = "mapping", ARTIFACT_STANDARD = "%s-%s.jar", ARTIFACT_UNOPTIMIZED = "%s-unoptimized-%s.jar", ARTIFACT_API = "%s-api-%s.jar", ARTIFACT_STANDALONE = "%s-standalone-%s.jar"; protected String artifactName, artifactVersion; protected Path artifactPath, artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, // these are different for forge builds proguardOut; @Input @Optional protected String compType = null; public String getCompType() { return compType; } public void setCompType(String compType) { this.compType = compType; } public BaritoneGradleTask() { this.artifactName = getProject().getRootProject().getProperties().get("archives_base_name").toString(); } public void doFirst() { if (compType != null) { this.artifactVersion = compType + "-" + getProject().getVersion(); } else { this.artifactVersion = getProject().getVersion().toString(); } this.artifactPath = this.getBuildFile(formatVersion(ARTIFACT_STANDARD)); this.artifactUnoptimizedPath = this.getBuildFile(formatVersion(ARTIFACT_UNOPTIMIZED)); this.artifactApiPath = this.getBuildFile(formatVersion(ARTIFACT_API)); this.artifactStandalonePath = this.getBuildFile(formatVersion(ARTIFACT_STANDALONE)); this.proguardOut = this.getTemporaryFile(PROGUARD_EXPORT_PATH); } protected void verifyArtifacts() throws IllegalStateException { if (!Files.exists(this.artifactPath)) { throw new IllegalStateException("Artifact not found! Run build first! Missing file: " + this.artifactPath); } } protected void write(InputStream stream, Path file) throws IOException { if (Files.exists(file)) { Files.delete(file); } Files.copy(stream, file); } protected String formatVersion(String string) { return String.format(string, this.artifactName, this.artifactVersion); } protected Path getRelativeFile(String file) { return Paths.get(new File(getProject().getBuildDir(), file).getAbsolutePath()); } protected Path getRootRelativeFile(String file) { return Paths.get(new File(getProject().getRootDir(), file).getAbsolutePath()); } protected Path getTemporaryFile(String file) { return Paths.get(new File(getTemporaryDir(), file).getAbsolutePath()); } protected Path getBuildFile(String file) { return getRelativeFile("libs/" + file); } protected String addCompTypeFirst(String string) { return compType == null ? string : compType + "-" + string; } } ================================================ FILE: buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.gradle.task; import org.gradle.api.tasks.TaskAction; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; /** * @author Brady * @since 10/12/2018 */ public class CreateDistTask extends BaritoneGradleTask { private static MessageDigest SHA1_DIGEST; @TaskAction protected void exec() throws Exception { super.doFirst(); super.verifyArtifacts(); // Define the distribution file paths Path api = getRootRelativeFile("dist/" + getFileName(artifactApiPath)); Path standalone = getRootRelativeFile("dist/" + getFileName(artifactStandalonePath)); Path unoptimized = getRootRelativeFile("dist/" + getFileName(artifactUnoptimizedPath)); // NIO will not automatically create directories Path dir = getRootRelativeFile("dist/"); if (!Files.exists(dir)) { Files.createDirectory(dir); } // Copy build jars to dist/ // TODO: dont copy files that dont exist Files.copy(this.artifactApiPath, api, REPLACE_EXISTING); Files.copy(this.artifactStandalonePath, standalone, REPLACE_EXISTING); Files.copy(this.artifactUnoptimizedPath, unoptimized, REPLACE_EXISTING); // Calculate all checksums and format them like "shasum" List<String> shasum = Files.list(getRootRelativeFile("dist/")) .filter(e -> e.getFileName().toString().endsWith(".jar")) .map(path -> sha1(path) + " " + path.getFileName().toString()) .collect(Collectors.toList()); shasum.forEach(System.out::println); // Write the checksums to a file Files.write(getRootRelativeFile("dist/checksums.txt"), shasum); } private static String getFileName(Path p) { return p.getFileName().toString(); } private static synchronized String sha1(Path path) { try { if (SHA1_DIGEST == null) { SHA1_DIGEST = MessageDigest.getInstance("SHA-1"); } return bytesToHex(SHA1_DIGEST.digest(Files.readAllBytes(path))).toLowerCase(); } catch (Exception e) { // haha no thanks throw new IllegalStateException(e); } } private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); public static String bytesToHex(byte[] bytes) { byte[] hexChars = new byte[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = HEX_ARRAY[v >>> 4]; hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; } return new String(hexChars, StandardCharsets.UTF_8); } } ================================================ FILE: buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.gradle.task; import baritone.gradle.util.Determinizer; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.SourceSetContainer; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskCollection; import org.gradle.api.tasks.compile.ForkOptions; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.internal.jvm.Jvm; import org.gradle.jvm.toolchain.JavaLanguageVersion; import org.gradle.jvm.toolchain.JavaLauncher; import org.gradle.jvm.toolchain.JavaToolchainService; import xyz.wagyourtail.unimined.api.UniminedExtension; import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig; import java.io.*; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * @author Brady * @since 10/11/2018 */ public class ProguardTask extends BaritoneGradleTask { @Input private String proguardVersion; public String getProguardVersion() { return proguardVersion; } private List<String> requiredLibraries; @TaskAction protected void exec() throws Exception { super.doFirst(); super.verifyArtifacts(); // "Haha brady why don't you make separate tasks" downloadProguard(); extractProguard(); generateConfigs(); processArtifact(); proguardApi(); proguardStandalone(); cleanup(); } UniminedExtension ext = getProject().getExtensions().getByType(UniminedExtension.class); SourceSetContainer sourceSets = getProject().getExtensions().getByType(SourceSetContainer.class); private File getMcJar() { MinecraftConfig mcc = ext.getMinecrafts().get(sourceSets.getByName("main")); return mcc.getMinecraft(mcc.getMcPatcher().getProdNamespace(), mcc.getMcPatcher().getProdNamespace()).toFile(); } private boolean isMcJar(File f) { MinecraftConfig mcc = ext.getMinecrafts().get(sourceSets.getByName("main")); return mcc.isMinecraftJar(f.toPath()); } private void processArtifact() throws Exception { if (Files.exists(this.artifactUnoptimizedPath)) { Files.delete(this.artifactUnoptimizedPath); } Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString(), List.of(), false); } private void downloadProguard() throws Exception { Path proguardZip = getTemporaryFile(String.format(PROGUARD_ZIP, proguardVersion)); if (!Files.exists(proguardZip)) { write(new URL(String.format("https://github.com/Guardsquare/proguard/releases/download/v%s/proguard-%s.zip", proguardVersion, proguardVersion)).openStream(), proguardZip); } } private void extractProguard() throws Exception { Path proguardJar = getTemporaryFile(String.format(PROGUARD_JAR, proguardVersion)); if (!Files.exists(proguardJar)) { ZipFile zipFile = new ZipFile(getTemporaryFile(String.format(PROGUARD_ZIP, proguardVersion)).toFile()); ZipEntry zipJarEntry = zipFile.getEntry(String.format("proguard-%s/lib/proguard.jar", proguardVersion)); write(zipFile.getInputStream(zipJarEntry), proguardJar); zipFile.close(); } } private JavaLauncher getJavaLauncherForProguard() { var toolchains = getProject().getExtensions().getByType(JavaToolchainService.class); var toolchain = toolchains.launcherFor((spec) -> { spec.getLanguageVersion().set(JavaLanguageVersion.of(getProject().findProperty("java_version").toString())); }).getOrNull(); if (toolchain == null) { throw new IllegalStateException("Java toolchain not found"); } return toolchain; } private void generateConfigs() throws Exception { Files.copy(getRootRelativeFile(PROGUARD_CONFIG_TEMPLATE), getTemporaryFile(PROGUARD_CONFIG_DEST), StandardCopyOption.REPLACE_EXISTING); // Setup the template that will be used to derive the API and Standalone configs List<String> template = Files.readAllLines(getTemporaryFile(PROGUARD_CONFIG_DEST)); template.add(0, "-injars '" + this.artifactPath.toString() + "'"); template.add(1, "-outjars '" + this.getTemporaryFile(PROGUARD_EXPORT_PATH) + "'"); template.add(2, "-libraryjars <java.home>/jmods/java.base.jmod(!**.jar;!module-info.class)"); template.add(3, "-libraryjars <java.home>/jmods/java.desktop.jmod(!**.jar;!module-info.class)"); template.add(4, "-libraryjars <java.home>/jmods/jdk.unsupported.jmod(!**.jar;!module-info.class)"); { final Stream<File> libraries; File mcJar; try { mcJar = getMcJar(); } catch (Exception e) { throw new RuntimeException("Failed to find Minecraft jar", e); } { // Discover all of the libraries that we will need to acquire from gradle final Stream<File> dependencies = acquireDependencies() // remove MCP mapped jar, and nashorn .filter(f -> !f.toString().endsWith("-recomp.jar") && !f.getName().startsWith("nashorn") && !f.getName().startsWith("coremods")); libraries = dependencies .map(f -> isMcJar(f) ? mcJar : f); } libraries.forEach(f -> { template.add(2, "-libraryjars '" + f + "'"); }); } Files.createDirectories(this.getRootRelativeFile(PROGUARD_MAPPING_DIR)); List<String> api = new ArrayList<>(template); api.add(2, "-printmapping " + new File(this.getRootRelativeFile(PROGUARD_MAPPING_DIR).toFile(), "mappings-" + addCompTypeFirst("api.txt"))); // API config doesn't require any changes from the changes that we made to the template Files.write(getTemporaryFile(compType + PROGUARD_API_CONFIG), api); // For the Standalone config, don't keep the API package List<String> standalone = new ArrayList<>(template); standalone.removeIf(s -> s.contains("# this is the keep api")); standalone.add(2, "-printmapping " + new File(this.getRootRelativeFile(PROGUARD_MAPPING_DIR).toFile(), "mappings-" + addCompTypeFirst("standalone.txt"))); Files.write(getTemporaryFile(compType + PROGUARD_STANDALONE_CONFIG), standalone); } private Stream<File> acquireDependencies() { return getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().findByName("main").getCompileClasspath().getFiles() .stream() .filter(File::isFile); } private void proguardApi() throws Exception { runProguard(getTemporaryFile(compType + PROGUARD_API_CONFIG)); Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString(), List.of(), false); } private void proguardStandalone() throws Exception { runProguard(getTemporaryFile(compType + PROGUARD_STANDALONE_CONFIG)); Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString(), List.of(), false); } private static final class Pair<A, B> { public final A a; public final B b; private Pair(final A a, final B b) { this.a = a; this.b = b; } @Override public String toString() { return "Pair{" + "a=" + this.a + ", " + "b=" + this.b + '}'; } } private void cleanup() { try { Files.delete(this.proguardOut); } catch (IOException ignored) {} } public void setProguardVersion(String url) { this.proguardVersion = url; } private void runProguard(Path config) throws Exception { // Delete the existing proguard output file. Proguard probably handles this already, but why not do it ourselves if (Files.exists(this.proguardOut)) { Files.delete(this.proguardOut); } Path workingDirectory = getTemporaryFile(""); getProject().javaexec(spec -> { spec.workingDir(workingDirectory.toFile()); spec.args("@" + workingDirectory.relativize(config)); spec.classpath(getTemporaryFile(String.format(PROGUARD_JAR, proguardVersion))); spec.executable(getJavaLauncherForProguard().getExecutablePath().getAsFile()); }).assertNormalExitValue().rethrowFailure(); } } ================================================ FILE: buildSrc/src/main/java/baritone/gradle/util/Determinizer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.gradle.util; import com.google.gson.*; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.*; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; /** * Make a .jar file deterministic by sorting all entries by name, and setting all the last modified times to 42069. * This makes the build 100% reproducible since the timestamp when you built it no longer affects the final file. * * @author leijurv */ public class Determinizer { public static void determinize(String inputPath, String outputPath, List<File> toInclude, boolean doForgeReplacementOfMetaInf) throws IOException { System.out.println("Running Determinizer"); System.out.println(" Input path: " + inputPath); System.out.println(" Output path: " + outputPath); System.out.println(" Shade: " + toInclude); try ( JarFile jarFile = new JarFile(new File(inputPath)); JarOutputStream jos = new JarOutputStream(new FileOutputStream(new File(outputPath))) ) { List<JarEntry> entries = jarFile.stream() .sorted(Comparator.comparing(JarEntry::getName)) .collect(Collectors.toList()); for (JarEntry entry : entries) { if (entry.getName().equals("META-INF/fml_cache_annotation.json")) { continue; } if (entry.getName().equals("META-INF/fml_cache_class_versions.json")) { continue; } JarEntry clone = new JarEntry(entry.getName()); clone.setTime(42069); jos.putNextEntry(clone); if (entry.getName().endsWith(".refmap.json")) { JsonElement json = new JsonParser().parse(new InputStreamReader(jarFile.getInputStream(entry))); jos.write(writeSorted(json).getBytes()); } else if (entry.getName().equals("META-INF/MANIFEST.MF") && doForgeReplacementOfMetaInf) { // only replace for forge jar ByteArrayOutputStream cancer = new ByteArrayOutputStream(); copy(jarFile.getInputStream(entry), cancer); String manifest = new String(cancer.toByteArray()); if (!manifest.contains("baritone.launch.tweaker.BaritoneTweaker")) { throw new IllegalStateException("unable to replace"); } manifest = manifest.replace("baritone.launch.tweaker.BaritoneTweaker", "org.spongepowered.asm.launch.MixinTweaker"); jos.write(manifest.getBytes()); } else { copy(jarFile.getInputStream(entry), jos); } } for (File file : toInclude) { try (JarFile mixin = new JarFile(file)) { for (JarEntry entry : mixin.stream().sorted(Comparator.comparing(JarEntry::getName)).collect(Collectors.toList())) { if (entry.getName().startsWith("META-INF") && !entry.getName().startsWith("META-INF/services")) { continue; } jos.putNextEntry(entry); copy(mixin.getInputStream(entry), jos); } } } jos.finish(); } System.out.println("Done with determinizer"); } private static void copy(InputStream is, OutputStream os) throws IOException { byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } } private static String writeSorted(JsonElement in) throws IOException { StringWriter writer = new StringWriter(); JsonWriter jw = new JsonWriter(writer); ORDERED_JSON_WRITER.write(jw, in); return writer.toString() + "\n"; } /** * All credits go to GSON and its contributors. GSON is licensed under the Apache 2.0 License. * This implementation has been modified to write {@link JsonObject} keys in order. * * @see <a href="https://github.com/google/gson/blob/master/LICENSE">GSON License</a> * @see <a href="https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java#L698">Original Source</a> */ private static final TypeAdapter<JsonElement> ORDERED_JSON_WRITER = new TypeAdapter<JsonElement>() { @Override public JsonElement read(JsonReader in) { return null; } @Override public void write(JsonWriter out, JsonElement value) throws IOException { if (value == null || value.isJsonNull()) { out.nullValue(); } else if (value.isJsonPrimitive()) { JsonPrimitive primitive = value.getAsJsonPrimitive(); if (primitive.isNumber()) { out.value(primitive.getAsNumber()); } else if (primitive.isBoolean()) { out.value(primitive.getAsBoolean()); } else { out.value(primitive.getAsString()); } } else if (value.isJsonArray()) { out.beginArray(); for (JsonElement e : value.getAsJsonArray()) { write(out, e); } out.endArray(); } else if (value.isJsonObject()) { out.beginObject(); List<Map.Entry<String, JsonElement>> entries = new ArrayList<>(value.getAsJsonObject().entrySet()); entries.sort(Comparator.comparing(Map.Entry::getKey)); for (Map.Entry<String, JsonElement> e : entries) { out.name(e.getKey()); write(out, e.getValue()); } out.endObject(); } else { throw new IllegalArgumentException("Couldn't write " + value.getClass()); } } }; } ================================================ FILE: fabric/build.gradle ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ import baritone.gradle.task.CreateDistTask import baritone.gradle.task.ProguardTask plugins { id "com.github.johnrengelman.shadow" version "8.0.0" } archivesBaseName = archivesBaseName + "-fabric" unimined.minecraft { fabric { loader project.fabric_version } } configurations { common shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common } dependencies { // because of multiple sourcesets `common project(":")` doesn't work for (sourceSet in rootProject.sourceSets) { if (sourceSet == rootProject.sourceSets.test) continue if (sourceSet == rootProject.sourceSets.schematica_api) continue common sourceSet.output shadowCommon sourceSet.output } include "dev.babbaj:nether-pathfinder:${project.nether_pathfinder_version}" } processResources { inputs.property "version", project.version filesMatching("fabric.mod.json") { expand "version": project.version } } shadowJar { configurations = [project.configurations.shadowCommon] archiveClassifier.set "dev-shadow" } remapJar { inputFile.set shadowJar.archiveFile dependsOn shadowJar archiveClassifier.set null } jar { archiveClassifier.set "dev" } components.java { withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { skip() } } task proguard(type: ProguardTask) { proguardVersion "7.2.1" compType "fabric" } task createDist(type: CreateDistTask, dependsOn: proguard) { compType "fabric" } build.finalizedBy(createDist) publishing { publications { mavenFabric(MavenPublication) { artifactId = rootProject.archives_base_name + "-" + project.name from components.java } } // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. repositories { // Add repositories to publish to here. } } ================================================ FILE: fabric/src/main/resources/fabric.mod.json ================================================ { "schemaVersion": 1, "id": "baritone", "version": "${version}", "name": "Baritone", "description": "Google Maps for Blockgame", "authors": [ "leijurv", "Brady" ], "contact": { "homepage": "https://github.com/cabaletta/baritone", "sources": "https://github.com/cabaletta/baritone", "issues": "https://github.com/cabaletta/baritone/issues" }, "license": "LGPL-3.0", "icon": "assets/baritone/icon.png", "environment": "*", "entrypoints": { }, "mixins": [ "mixins.baritone.json" ], "depends": { "fabricloader": ">=0.11.0", "minecraft": "1.19.4" }, "custom": { "modmenu": { "links": { "modmenu.discord": "https://discord.gg/s6fRBAUpmr" } } } } ================================================ FILE: forge/build.gradle ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ import baritone.gradle.task.CreateDistTask import baritone.gradle.task.ProguardTask plugins { id "com.github.johnrengelman.shadow" version "8.0.0" } archivesBaseName = archivesBaseName + "-forge" unimined.minecraft { mappings { devFallbackNamespace "intermediary" } forge { loader project.forge_version mixinConfig ["mixins.baritone.json"] } } //loom { // forge { // mixinConfig 'mixins.baritone.json' // } //} configurations { common shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common } dependencies { // because of multiple sourcesets `common project(":")` doesn't work for (sourceSet in rootProject.sourceSets) { if (sourceSet == rootProject.sourceSets.test) continue if (sourceSet == rootProject.sourceSets.schematica_api) continue common sourceSet.output shadowCommon sourceSet.output } shadowCommon "dev.babbaj:nether-pathfinder:${project.nether_pathfinder_version}" } processResources { inputs.property "version", project.version filesMatching("META-INF/mods.toml") { expand "version": project.version } } shadowJar { configurations = [project.configurations.shadowCommon] archiveClassifier.set "dev-shadow" } remapJar { inputFile.set shadowJar.archiveFile dependsOn shadowJar archiveClassifier.set null } jar { archiveClassifier.set "dev" manifest { attributes( 'MixinConfigs': 'mixins.baritone.json', "MixinConnector": "baritone.launch.BaritoneMixinConnector", 'Implementation-Title': 'Baritone', 'Implementation-Version': version, ) } } components.java { withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { skip() } } task proguard(type: ProguardTask) { proguardVersion "7.2.1" compType "forge" } task createDist(type: CreateDistTask, dependsOn: proguard) { compType "forge" } build.finalizedBy(createDist) publishing { publications { mavenFabric(MavenPublication) { artifactId = rootProject.archives_base_name + "-" + project.name from components.java } } // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. repositories { // Add repositories to publish to here. } } ================================================ FILE: forge/gradle.properties ================================================ # # 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 <https://www.gnu.org/licenses/>. # loom.platform=forge ================================================ FILE: forge/src/main/java/baritone/launch/BaritoneForgeModXD.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch;import net.minecraftforge.fml.common.Mod; @Mod("baritoe") public class BaritoneForgeModXD { } ================================================ FILE: forge/src/main/resources/META-INF/mods.toml ================================================ # This is an example mods.toml file. It contains the data relating to the loading mods. # There are several mandatory fields (#mandatory), and many more that are optional (#optional). # The overall format is standard TOML format, v0.5.0. # Note that there are a couple of TOML lists in this file. # Find more information on toml format here: https://github.com/toml-lang/toml # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml modLoader="javafml" #mandatory # A version range to match for said mod loader - for regular FML @Mod it will be the forge version loaderVersion="[33,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. license="https://raw.githubusercontent.com/cabaletta/baritone/1.16.2/LICENSE" # A URL to refer people to when problems occur with this mod issueTrackerURL="https://github.com/cabaletta/baritone/issues" #optional # A list of mods - how many allowed here is determined by the individual mod loader [[mods]] #mandatory # The modid of the mod modId="baritoe" #mandatory # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it version="${version}" #mandatory # A display name for the mod displayName="Baritone" #mandatory # A URL for the "homepage" for this mod, displayed in the mod UI displayURL="https://github.com/cabaletta/baritone" #optional # A file name (in the root of the mod JAR) containing a logo for display #logoFile="examplemod.png" #optional # A text field displayed in the mod UI credits="Hat Gamers" #optional # A text field displayed in the mod UI authors="leijurv, Brady" #optional # The description text for the mod (multi line!) (#mandatory) description=''' A Minecraft pathfinder bot. ''' [[dependencies.baritoe]] modId="minecraft" mandatory=true # This version range declares a minimum of the current minecraft version up to but not including the next major version versionRange="[1.19.4]" ordering="NONE" side="BOTH" ================================================ FILE: forge/src/main/resources/pack.mcmeta ================================================ { "pack": { "description": "null", "pack_format": 8 } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ org.gradle.jvmargs=-Xmx4G mod_version=1.9.5 maven_group=baritone archives_base_name=baritone java_version=17 minecraft_version=1.19.4 forge_version=45.0.43 fabric_version=0.14.11 nether_pathfinder_version=1.4.1 // These dependencies are used for common and tweaker // while mod loaders usually ship their own version mixin_version=0.8.5 asm_version=9.3 ================================================ FILE: gradlew ================================================ #!/usr/bin/env sh # # Copyright 2015 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MSYS* | MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin or MSYS, switch paths to Windows format before running java if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=`expr $i + 1` done case $i in 0) set -- ;; 1) set -- "$args0" ;; 2) set -- "$args0" "$args1" ;; 3) set -- "$args0" "$args1" "$args2" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: jitpack.yml ================================================ before_install: - curl -s "https://get.sdkman.io" | bash - sdk install java 17.0.5-tem - sdk use java 17.0.5-tem ================================================ FILE: scripts/proguard.pro ================================================ -keepattributes Signature -keepattributes *Annotation* -keepattributes InnerClasses -optimizationpasses 5 -verbose -allowaccessmodification # anything not kept can be changed from public to private and inlined etc -overloadaggressively -dontusemixedcaseclassnames # instead of renaming to a, b, c, rename to baritone.a, baritone.b, baritone.c so as to not conflict with minecraft's obfd classes -flattenpackagehierarchy -repackageclasses 'baritone' # lwjgl is weird -dontwarn org.lwjgl.** # also lwjgl lol -dontwarn module-info # we dont have forge -dontwarn baritone.launch.BaritoneForgeModXD # progard doesn't like signature polymorphism -dontwarn java.lang.invoke.MethodHandle # please do not change the comment below -keep class baritone.api.** { *; } # this is the keep api # service provider needs these class names -keep class baritone.BaritoneProvider -keep class baritone.api.IBaritoneProvider -keep class baritone.api.utils.MyChunkPos { *; } # even in standalone we need to keep this for gson reflect -keepname class baritone.api.utils.BlockOptionalMeta # this name is exposed to the user, so we need to keep it in all builds # Keep any class or member annotated with @KeepName so we dont have to put everything in the script -keep,allowobfuscation @interface baritone.KeepName -keep @baritone.KeepName class * -keepclassmembers class * { @baritone.KeepName *; } # setting names are reflected from field names, so keep field names -keepclassmembers class baritone.api.Settings { public <fields>; } # need to keep mixin names -keep class baritone.launch.** { *; } #try to keep usage of schematica in separate classes -keep class baritone.utils.schematic.schematica.** -keep class baritone.utils.schematic.litematica.** #proguard doesnt like it when it cant find our fake schematica classes -dontwarn baritone.utils.schematic.schematica.** -dontwarn baritone.utils.schematic.litematica.** # nether-pathfinder uses JNI to acess its own classes # and some of our builds include it before running proguard # conservatively keep all of it, even though only PathSegment.<init> is needed -keep,allowoptimization class dev.babbaj.pathfinder.** { *; } # Keep - Applications. Keep all application classes, along with their 'main' # methods. -keepclasseswithmembers public class * { public static void main(java.lang.String[]); } # Also keep - Enumerations. Keep the special static methods that are required in # enumeration classes. -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # Also keep - Database drivers. Keep all implementations of java.sql.Driver. -keep class * extends java.sql.Driver # Also keep - Swing UI L&F. Keep all extensions of javax.swing.plaf.ComponentUI, # along with the special 'createUI' method. -keep class * extends javax.swing.plaf.ComponentUI { public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent); } # Keep names - Native method names. Keep all native class/method names. -keepclasseswithmembers,includedescriptorclasses,allowshrinking class * { native <methods>; } # Remove - System method calls. Remove all invocations of System # methods without side effects whose return values are not used. -assumenosideeffects public class java.lang.System { public static long currentTimeMillis(); static java.lang.Class getCallerClass(); public static int identityHashCode(java.lang.Object); public static java.lang.SecurityManager getSecurityManager(); public static java.util.Properties getProperties(); public static java.lang.String getProperty(java.lang.String); public static java.lang.String getenv(java.lang.String); public static java.lang.String mapLibraryName(java.lang.String); public static java.lang.String getProperty(java.lang.String,java.lang.String); } # Remove - Math method calls. Remove all invocations of Math # methods without side effects whose return values are not used. -assumenosideeffects public class java.lang.Math { public static double sin(double); public static double cos(double); public static double tan(double); public static double asin(double); public static double acos(double); public static double atan(double); public static double toRadians(double); public static double toDegrees(double); public static double exp(double); public static double log(double); public static double log10(double); public static double sqrt(double); public static double cbrt(double); public static double IEEEremainder(double,double); public static double ceil(double); public static double floor(double); public static double rint(double); public static double atan2(double,double); public static double pow(double,double); public static int round(float); public static long round(double); public static double random(); public static int abs(int); public static long abs(long); public static float abs(float); public static double abs(double); public static int max(int,int); public static long max(long,long); public static float max(float,float); public static double max(double,double); public static int min(int,int); public static long min(long,long); public static float min(float,float); public static double min(double,double); public static double ulp(double); public static float ulp(float); public static double signum(double); public static float signum(float); public static double sinh(double); public static double cosh(double); public static double tanh(double); public static double hypot(double,double); public static double expm1(double); public static double log1p(double); } # Remove - Number method calls. Remove all invocations of Number # methods without side effects whose return values are not used. -assumenosideeffects public class java.lang.* extends java.lang.Number { public static java.lang.String toString(byte); public static java.lang.Byte valueOf(byte); # public static byte parseByte(java.lang.String); # public static byte parseByte(java.lang.String,int); # public static java.lang.Byte valueOf(java.lang.String,int); # public static java.lang.Byte valueOf(java.lang.String); # public static java.lang.Byte decode(java.lang.String); public int compareTo(java.lang.Byte); public static java.lang.String toString(short); # public static short parseShort(java.lang.String); # public static short parseShort(java.lang.String,int); # public static java.lang.Short valueOf(java.lang.String,int); # public static java.lang.Short valueOf(java.lang.String); public static java.lang.Short valueOf(short); # public static java.lang.Short decode(java.lang.String); public static short reverseBytes(short); public int compareTo(java.lang.Short); public static java.lang.String toString(int,int); public static java.lang.String toHexString(int); public static java.lang.String toOctalString(int); public static java.lang.String toBinaryString(int); public static java.lang.String toString(int); # public static int parseInt(java.lang.String,int); # public static int parseInt(java.lang.String); # public static java.lang.Integer valueOf(java.lang.String,int); # public static java.lang.Integer valueOf(java.lang.String); public static java.lang.Integer valueOf(int); public static java.lang.Integer getInteger(java.lang.String); public static java.lang.Integer getInteger(java.lang.String,int); public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer); public static java.lang.Integer decode(java.lang.String); public static int highestOneBit(int); public static int lowestOneBit(int); public static int numberOfLeadingZeros(int); public static int numberOfTrailingZeros(int); public static int bitCount(int); public static int rotateLeft(int,int); public static int rotateRight(int,int); public static int reverse(int); public static int signum(int); public static int reverseBytes(int); public int compareTo(java.lang.Integer); public static java.lang.String toString(long,int); public static java.lang.String toHexString(long); public static java.lang.String toOctalString(long); public static java.lang.String toBinaryString(long); public static java.lang.String toString(long); # public static long parseLong(java.lang.String,int); # public static long parseLong(java.lang.String); # public static java.lang.Long valueOf(java.lang.String,int); # public static java.lang.Long valueOf(java.lang.String); public static java.lang.Long valueOf(long); # public static java.lang.Long decode(java.lang.String); public static java.lang.Long getLong(java.lang.String); public static java.lang.Long getLong(java.lang.String,long); public static java.lang.Long getLong(java.lang.String,java.lang.Long); public static long highestOneBit(long); public static long lowestOneBit(long); public static int numberOfLeadingZeros(long); public static int numberOfTrailingZeros(long); public static int bitCount(long); public static long rotateLeft(long,int); public static long rotateRight(long,int); public static long reverse(long); public static int signum(long); public static long reverseBytes(long); public int compareTo(java.lang.Long); public static java.lang.String toString(float); public static java.lang.String toHexString(float); # public static java.lang.Float valueOf(java.lang.String); public static java.lang.Float valueOf(float); # public static float parseFloat(java.lang.String); public static boolean isNaN(float); public static boolean isInfinite(float); public static int floatToIntBits(float); public static int floatToRawIntBits(float); public static float intBitsToFloat(int); public static int compare(float,float); public boolean isNaN(); public boolean isInfinite(); public int compareTo(java.lang.Float); public static java.lang.String toString(double); public static java.lang.String toHexString(double); # public static java.lang.Double valueOf(java.lang.String); # public static java.lang.Double valueOf(double); # public static double parseDouble(java.lang.String); public static boolean isNaN(double); public static boolean isInfinite(double); public static long doubleToLongBits(double); public static long doubleToRawLongBits(double); public static double longBitsToDouble(long); public static int compare(double,double); public boolean isNaN(); public boolean isInfinite(); public int compareTo(java.lang.Double); public byte byteValue(); public short shortValue(); public int intValue(); public long longValue(); public float floatValue(); public double doubleValue(); public int compareTo(java.lang.Object); public boolean equals(java.lang.Object); public int hashCode(); public java.lang.String toString(); } # Remove - String method calls. Remove all invocations of String # methods without side effects whose return values are not used. -assumenosideeffects public class java.lang.String { public static java.lang.String copyValueOf(char[]); public static java.lang.String copyValueOf(char[],int,int); public static java.lang.String valueOf(boolean); public static java.lang.String valueOf(char); public static java.lang.String valueOf(char[]); public static java.lang.String valueOf(char[],int,int); public static java.lang.String valueOf(double); public static java.lang.String valueOf(float); public static java.lang.String valueOf(int); public static java.lang.String valueOf(java.lang.Object); public static java.lang.String valueOf(long); public boolean contentEquals(java.lang.StringBuffer); public boolean endsWith(java.lang.String); public boolean equalsIgnoreCase(java.lang.String); public boolean equals(java.lang.Object); public boolean matches(java.lang.String); public boolean regionMatches(boolean,int,java.lang.String,int,int); public boolean regionMatches(int,java.lang.String,int,int); public boolean startsWith(java.lang.String); public boolean startsWith(java.lang.String,int); public byte[] getBytes(); public byte[] getBytes(java.lang.String); public char charAt(int); public char[] toCharArray(); public int compareToIgnoreCase(java.lang.String); public int compareTo(java.lang.Object); public int compareTo(java.lang.String); public int hashCode(); public int indexOf(int); public int indexOf(int,int); public int indexOf(java.lang.String); public int indexOf(java.lang.String,int); public int lastIndexOf(int); public int lastIndexOf(int,int); public int lastIndexOf(java.lang.String); public int lastIndexOf(java.lang.String,int); public int length(); public java.lang.CharSequence subSequence(int,int); public java.lang.String concat(java.lang.String); public java.lang.String replaceAll(java.lang.String,java.lang.String); public java.lang.String replace(char,char); public java.lang.String replaceFirst(java.lang.String,java.lang.String); public java.lang.String[] split(java.lang.String); public java.lang.String[] split(java.lang.String,int); public java.lang.String substring(int); public java.lang.String substring(int,int); public java.lang.String toLowerCase(); public java.lang.String toLowerCase(java.util.Locale); public java.lang.String toString(); public java.lang.String toUpperCase(); public java.lang.String toUpperCase(java.util.Locale); public java.lang.String trim(); } # Remove - StringBuffer method calls. Remove all invocations of StringBuffer # methods without side effects whose return values are not used. -assumenosideeffects public class java.lang.StringBuffer { public java.lang.String toString(); public char charAt(int); public int capacity(); public int codePointAt(int); public int codePointBefore(int); public int indexOf(java.lang.String,int); public int lastIndexOf(java.lang.String); public int lastIndexOf(java.lang.String,int); public int length(); public java.lang.String substring(int); public java.lang.String substring(int,int); } # Remove - StringBuilder method calls. Remove all invocations of StringBuilder # methods without side effects whose return values are not used. -assumenosideeffects public class java.lang.StringBuilder { public java.lang.String toString(); public char charAt(int); public int capacity(); public int codePointAt(int); public int codePointBefore(int); public int indexOf(java.lang.String,int); public int lastIndexOf(java.lang.String); public int lastIndexOf(java.lang.String,int); public int length(); public java.lang.String substring(int); public java.lang.String substring(int,int); } ================================================ FILE: settings.gradle ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ pluginManagement { repositories { mavenLocal() maven { name = 'WagYourMaven' url = 'https://maven.wagyourtail.xyz/snapshots' } maven { name = 'ForgeMaven' url = 'https://maven.minecraftforge.net/' } maven { name = 'FabricMaven' url = 'https://maven.fabricmc.net/' } mavenCentral() gradlePluginPortal() { content { excludeGroup "org.apache.logging.log4j" } } } } rootProject.name = 'baritone' include("tweaker") if (System.getProperty("Baritone.enabled_platforms") == null) { System.setProperty("Baritone.enabled_platforms", "fabric,forge") } for (platform in System.getProperty("Baritone.enabled_platforms").split(",")) { include(platform) } ================================================ FILE: src/api/java/baritone/api/BaritoneAPI.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api; import baritone.api.utils.SettingsUtil; /** * Exposes the {@link IBaritoneProvider} instance and the {@link Settings} instance for API usage. * * @author Brady * @since 9/23/2018 */ public final class BaritoneAPI { private static final IBaritoneProvider provider; private static final Settings settings; static { settings = new Settings(); SettingsUtil.readAndApply(settings, SettingsUtil.SETTINGS_DEFAULT_NAME); try { provider = (IBaritoneProvider) Class.forName("baritone.BaritoneProvider").newInstance(); } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } } public static IBaritoneProvider getProvider() { return BaritoneAPI.provider; } public static Settings getSettings() { return BaritoneAPI.settings; } } ================================================ FILE: src/api/java/baritone/api/IBaritone.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api; import baritone.api.behavior.ILookBehavior; import baritone.api.behavior.IPathingBehavior; import baritone.api.cache.IWorldProvider; import baritone.api.command.manager.ICommandManager; import baritone.api.event.listener.IEventBus; import baritone.api.pathing.calc.IPathingControlManager; import baritone.api.process.*; import baritone.api.selection.ISelectionManager; import baritone.api.utils.IInputOverrideHandler; import baritone.api.utils.IPlayerContext; /** * @author Brady * @since 9/29/2018 */ public interface IBaritone { /** * @return The {@link IPathingBehavior} instance * @see IPathingBehavior */ IPathingBehavior getPathingBehavior(); /** * @return The {@link ILookBehavior} instance * @see ILookBehavior */ ILookBehavior getLookBehavior(); /** * @return The {@link IFollowProcess} instance * @see IFollowProcess */ IFollowProcess getFollowProcess(); /** * @return The {@link IMineProcess} instance * @see IMineProcess */ IMineProcess getMineProcess(); /** * @return The {@link IBuilderProcess} instance * @see IBuilderProcess */ IBuilderProcess getBuilderProcess(); /** * @return The {@link IExploreProcess} instance * @see IExploreProcess */ IExploreProcess getExploreProcess(); /** * @return The {@link IFarmProcess} instance * @see IFarmProcess */ IFarmProcess getFarmProcess(); /** * @return The {@link ICustomGoalProcess} instance * @see ICustomGoalProcess */ ICustomGoalProcess getCustomGoalProcess(); /** * @return The {@link IGetToBlockProcess} instance * @see IGetToBlockProcess */ IGetToBlockProcess getGetToBlockProcess(); /** * @return The {@link IElytraProcess} instance * @see IElytraProcess */ IElytraProcess getElytraProcess(); /** * @return The {@link IWorldProvider} instance * @see IWorldProvider */ IWorldProvider getWorldProvider(); /** * Returns the {@link IPathingControlManager} for this {@link IBaritone} instance, which is responsible * for managing the {@link IBaritoneProcess}es which control the {@link IPathingBehavior} state. * * @return The {@link IPathingControlManager} instance * @see IPathingControlManager */ IPathingControlManager getPathingControlManager(); /** * @return The {@link IInputOverrideHandler} instance * @see IInputOverrideHandler */ IInputOverrideHandler getInputOverrideHandler(); /** * @return The {@link IPlayerContext} instance * @see IPlayerContext */ IPlayerContext getPlayerContext(); /** * @return The {@link IEventBus} instance * @see IEventBus */ IEventBus getGameEventHandler(); /** * @return The {@link ISelectionManager} instance * @see ISelectionManager */ ISelectionManager getSelectionManager(); /** * @return The {@link ICommandManager} instance * @see ICommandManager */ ICommandManager getCommandManager(); /** * Open click */ void openClick(); } ================================================ FILE: src/api/java/baritone/api/IBaritoneProvider.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api; import baritone.api.cache.IWorldScanner; import baritone.api.command.ICommand; import baritone.api.command.ICommandSystem; import baritone.api.schematic.ISchematicSystem; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.player.LocalPlayer; import java.util.List; import java.util.Objects; /** * Provides the present {@link IBaritone} instances, as well as non-baritone instance related APIs. * * @author leijurv */ public interface IBaritoneProvider { /** * Returns the primary {@link IBaritone} instance. This instance is persistent, and * is represented by the local player that is created by the game itself, not a "bot" * player through Baritone. * * @return The primary {@link IBaritone} instance. */ IBaritone getPrimaryBaritone(); /** * Returns all of the active {@link IBaritone} instances. This includes the local one * returned by {@link #getPrimaryBaritone()}. * * @return All active {@link IBaritone} instances. * @see #getBaritoneForPlayer(LocalPlayer) */ List<IBaritone> getAllBaritones(); /** * Provides the {@link IBaritone} instance for a given {@link LocalPlayer}. * * @param player The player * @return The {@link IBaritone} instance. */ default IBaritone getBaritoneForPlayer(LocalPlayer player) { for (IBaritone baritone : this.getAllBaritones()) { if (Objects.equals(player, baritone.getPlayerContext().player())) { return baritone; } } return null; } /** * Provides the {@link IBaritone} instance for a given {@link Minecraft}. * * @param minecraft The minecraft * @return The {@link IBaritone} instance. */ default IBaritone getBaritoneForMinecraft(Minecraft minecraft) { for (IBaritone baritone : this.getAllBaritones()) { if (Objects.equals(minecraft, baritone.getPlayerContext().minecraft())) { return baritone; } } return null; } /** * Provides the {@link IBaritone} instance for the player with the specified connection. * * @param connection The connection * @return The {@link IBaritone} instance. */ default IBaritone getBaritoneForConnection(ClientPacketListener connection) { for (IBaritone baritone : this.getAllBaritones()) { final LocalPlayer player = baritone.getPlayerContext().player(); if (player != null && player.connection == connection) { return baritone; } } return null; } /** * Creates and registers a new {@link IBaritone} instance using the specified {@link Minecraft}. The existing * instance is returned if already registered. * * @param minecraft The minecraft * @return The {@link IBaritone} instance */ IBaritone createBaritone(Minecraft minecraft); /** * Destroys and removes the specified {@link IBaritone} instance. If the specified instance is the * {@link #getPrimaryBaritone() primary baritone}, this operation has no effect and will return {@code false}. * * @param baritone The baritone instance to remove * @return Whether the baritone instance was removed */ boolean destroyBaritone(IBaritone baritone); /** * Returns the {@link IWorldScanner} instance. This is not a type returned by * {@link IBaritone} implementation, because it is not linked with {@link IBaritone}. * * @return The {@link IWorldScanner} instance. */ IWorldScanner getWorldScanner(); /** * Returns the {@link ICommandSystem} instance. This is not bound to a specific {@link IBaritone} * instance because {@link ICommandSystem} itself controls global behavior for {@link ICommand}s. * * @return The {@link ICommandSystem} instance. */ ICommandSystem getCommandSystem(); /** * @return The {@link ISchematicSystem} instance. */ ISchematicSystem getSchematicSystem(); } ================================================ FILE: src/api/java/baritone/api/Settings.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api; import baritone.api.utils.Helper; import baritone.api.utils.NotificationHelper; import baritone.api.utils.SettingsUtil; import baritone.api.utils.TypeUtils; import baritone.api.utils.gui.BaritoneToast; import net.minecraft.client.GuiMessageTag; import net.minecraft.client.Minecraft; import net.minecraft.core.Vec3i; import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.*; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; /** * Baritone's settings. Settings apply to all Baritone instances. * * @author leijurv */ public final class Settings { private static final Logger LOGGER = LoggerFactory.getLogger("Baritone"); /** * Allow Baritone to break blocks */ public final Setting<Boolean> allowBreak = new Setting<>(true); /** * Blocks that baritone will be allowed to break even with allowBreak set to false */ public final Setting<List<Block>> allowBreakAnyway = new Setting<>(new ArrayList<>()); /** * Allow Baritone to sprint */ public final Setting<Boolean> allowSprint = new Setting<>(true); /** * Allow Baritone to place blocks */ public final Setting<Boolean> allowPlace = new Setting<>(true); /** * Allow Baritone to place blocks in fluid source blocks */ public final Setting<Boolean> allowPlaceInFluidsSource = new Setting<>(true); /** * Allow Baritone to place blocks in flowing fluid */ public final Setting<Boolean> allowPlaceInFluidsFlow = new Setting<>(true); /** * Allow Baritone to move items in your inventory to your hotbar */ public final Setting<Boolean> allowInventory = new Setting<>(false); /** * Wait this many ticks between InventoryBehavior moving inventory items */ public final Setting<Integer> ticksBetweenInventoryMoves = new Setting<>(1); /** * Come to a halt before doing any inventory moves. Intended for anticheat such as 2b2t */ public final Setting<Boolean> inventoryMoveOnlyIfStationary = new Setting<>(false); /** * Disable baritone's auto-tool at runtime, but still assume that another mod will provide auto tool functionality * <p> * Specifically, path calculation will still assume that an auto tool will run at execution time, even though * Baritone itself will not do that. */ public final Setting<Boolean> assumeExternalAutoTool = new Setting<>(false); /** * Automatically select the best available tool */ public final Setting<Boolean> autoTool = new Setting<>(true); /** * It doesn't actually take twenty ticks to place a block, this cost is so high * because we want to generally conserve blocks which might be limited. * <p> * Decrease to make Baritone more often consider paths that would require placing blocks */ public final Setting<Double> blockPlacementPenalty = new Setting<>(20D); /** * This is just a tiebreaker to make it less likely to break blocks if it can avoid it. * For example, fire has a break cost of 0, this makes it nonzero, so all else being equal * it will take an otherwise equivalent route that doesn't require it to put out fire. */ public final Setting<Double> blockBreakAdditionalPenalty = new Setting<>(2D); /** * Additional penalty for hitting the space bar (ascend, pillar, or parkour) because it uses hunger */ public final Setting<Double> jumpPenalty = new Setting<>(2D); /** * Walking on water uses up hunger really quick, so penalize it */ public final Setting<Double> walkOnWaterOnePenalty = new Setting<>(3D); /** * Don't allow breaking blocks next to liquids. * <p> * Enable if you have mods adding custom fluid physics. */ public final Setting<Boolean> strictLiquidCheck = new Setting<>(false); /** * Allow Baritone to fall arbitrary distances and place a water bucket beneath it. * Reliability: questionable. */ public final Setting<Boolean> allowWaterBucketFall = new Setting<>(true); /** * Allow Baritone to assume it can walk on still water just like any other block. * This functionality is assumed to be provided by a separate library that might have imported Baritone. * <p> * Note: This will prevent some usage of the frostwalker enchantment, like pillaring up from water. */ public final Setting<Boolean> assumeWalkOnWater = new Setting<>(false); /** * If you have Fire Resistance and Jesus then I guess you could turn this on lol */ public final Setting<Boolean> assumeWalkOnLava = new Setting<>(false); /** * Assume step functionality; don't jump on an Ascend. */ public final Setting<Boolean> assumeStep = new Setting<>(false); /** * Assume safe walk functionality; don't sneak on a backplace traverse. * <p> * Warning: if you do something janky like sneak-backplace from an ender chest, if this is true * it won't sneak right click, it'll just right click, which means it'll open the chest instead of placing * against it. That's why this defaults to off. */ public final Setting<Boolean> assumeSafeWalk = new Setting<>(false); /** * If true, parkour is allowed to make jumps when standing on blocks at the maximum height, so player feet is y=256 * <p> * Defaults to false because this fails on constantiam. Please let me know if this is ever disabled. Please. */ public final Setting<Boolean> allowJumpAtBuildLimit = new Setting<>(false); /** * Just here so mods that use the API don't break. Does nothing. */ @Deprecated @JavaOnly public final Setting<Boolean> allowJumpAt256 = new Setting<>(false); /** * This should be monetized it's so good * <p> * Defaults to true, but only actually takes effect if allowParkour is also true */ public final Setting<Boolean> allowParkourAscend = new Setting<>(true); /** * Allow descending diagonally * <p> * Safer than allowParkour yet still slightly unsafe, can make contact with unchecked adjacent blocks, so it's unsafe in the nether. * <p> * For a generic "take some risks" mode I'd turn on this one, parkour, and parkour place. */ public final Setting<Boolean> allowDiagonalDescend = new Setting<>(false); /** * Allow diagonal ascending * <p> * Actually pretty safe, much safer than diagonal descend tbh */ public final Setting<Boolean> allowDiagonalAscend = new Setting<>(false); /** * Allow mining the block directly beneath its feet * <p> * Turn this off to force it to make more staircases and less shafts */ public final Setting<Boolean> allowDownward = new Setting<>(true); /** * Blocks that Baritone is allowed to place (as throwaway, for sneak bridging, pillaring, etc.) */ public final Setting<List<Item>> acceptableThrowawayItems = new Setting<>(new ArrayList<>(Arrays.asList( Blocks.DIRT.asItem(), Blocks.COBBLESTONE.asItem(), Blocks.NETHERRACK.asItem(), Blocks.STONE.asItem() ))); /** * Blocks that Baritone will attempt to avoid (Used in avoidance) */ public final Setting<List<Block>> blocksToAvoid = new Setting<>(new ArrayList<>(List.of( Blocks.TRIPWIRE ))); /** * Blocks that Baritone is not allowed to break */ public final Setting<List<Block>> blocksToDisallowBreaking = new Setting<>(new ArrayList<>( // Leave Empty by Default )); /** * blocks that baritone shouldn't break, but can if it needs to. */ public final Setting<List<Block>> blocksToAvoidBreaking = new Setting<>(new ArrayList<>(Arrays.asList( // TODO can this be a HashSet or ImmutableSet? Blocks.CRAFTING_TABLE, Blocks.FURNACE, Blocks.CHEST, Blocks.TRAPPED_CHEST ))); /** * this multiplies the break speed, if set above 1 it's "encourage breaking" instead */ public final Setting<Double> avoidBreakingMultiplier = new Setting<>(.1); /** * A list of blocks to be treated as if they're air. * <p> * If a schematic asks for air at a certain position, and that position currently contains a block on this list, it will be treated as correct. */ public final Setting<List<Block>> buildIgnoreBlocks = new Setting<>(new ArrayList<>(Arrays.asList( ))); /** * A list of blocks to be treated as correct. * <p> * If a schematic asks for any block on this list at a certain position, it will be treated as correct, regardless of what it currently is. */ public final Setting<List<Block>> buildSkipBlocks = new Setting<>(new ArrayList<>(Arrays.asList( ))); /** * A mapping of blocks to blocks treated as correct in their position * <p> * If a schematic asks for a block on this mapping, all blocks on the mapped list will be accepted at that location as well * <p> * Syntax same as <a href="https://baritone.leijurv.com/baritone/api/Settings.html#buildSubstitutes">buildSubstitutes</a> */ public final Setting<Map<Block, List<Block>>> buildValidSubstitutes = new Setting<>(new HashMap<>()); /** * A mapping of blocks to blocks to be built instead * <p> * If a schematic asks for a block on this mapping, Baritone will place the first placeable block in the mapped list * <p> * Usage Syntax: * <pre> * sourceblockA->blockToSubstituteA1,blockToSubstituteA2,...blockToSubstituteAN,sourceBlockB->blockToSubstituteB1,blockToSubstituteB2,...blockToSubstituteBN,...sourceBlockX->blockToSubstituteX1,blockToSubstituteX2...blockToSubstituteXN * </pre> * Example: * <pre> * stone->cobblestone,andesite,oak_planks->birch_planks,acacia_planks,glass * </pre> */ public final Setting<Map<Block, List<Block>>> buildSubstitutes = new Setting<>(new HashMap<>()); /** * A list of blocks to become air * <p> * If a schematic asks for a block on this list, only air will be accepted at that location (and nothing on buildIgnoreBlocks) */ public final Setting<List<Block>> okIfAir = new Setting<>(new ArrayList<>(Arrays.asList( ))); /** * If this is true, the builder will treat all non-air blocks as correct. It will only place new blocks. */ public final Setting<Boolean> buildIgnoreExisting = new Setting<>(false); /** * If this is true, the builder will ignore directionality of certain blocks like glazed terracotta. */ public final Setting<Boolean> buildIgnoreDirection = new Setting<>(false); /** * A list of names of block properties the builder will ignore. */ public final Setting<List<String>> buildIgnoreProperties = new Setting<>(new ArrayList<>(Arrays.asList( ))); /** * If this setting is true, Baritone will never break a block that is adjacent to an unsupported falling block. * <p> * I.E. it will never trigger cascading sand / gravel falls */ public final Setting<Boolean> avoidUpdatingFallingBlocks = new Setting<>(true); /** * Enables some more advanced vine features. They're honestly just gimmicks and won't ever be needed in real * pathing scenarios. And they can cause Baritone to get trapped indefinitely in a strange scenario. * <p> * Almost never turn this on lol */ public final Setting<Boolean> allowVines = new Setting<>(false); /** * Slab behavior is complicated, disable this for higher path reliability. Leave enabled if you have bottom slabs * everywhere in your base. */ public final Setting<Boolean> allowWalkOnBottomSlab = new Setting<>(true); /** * You know what it is * <p> * But it's very unreliable and falls off when cornering like all the time so. * <p> * It also overshoots the landing pretty much always (making contact with the next block over), so be careful */ public final Setting<Boolean> allowParkour = new Setting<>(false); /** * Actually pretty reliable. * <p> * Doesn't make it any more dangerous compared to just normal allowParkour th */ public final Setting<Boolean> allowParkourPlace = new Setting<>(false); /** * For example, if you have Mining Fatigue or Haste, adjust the costs of breaking blocks accordingly. */ public final Setting<Boolean> considerPotionEffects = new Setting<>(true); /** * Sprint and jump a block early on ascends wherever possible */ public final Setting<Boolean> sprintAscends = new Setting<>(true); /** * If we overshoot a traverse and end up one block beyond the destination, mark it as successful anyway. * <p> * This helps with speed exceeding 20m/s */ public final Setting<Boolean> overshootTraverse = new Setting<>(true); /** * When breaking blocks for a movement, wait until all falling blocks have settled before continuing */ public final Setting<Boolean> pauseMiningForFallingBlocks = new Setting<>(true); /** * How many ticks between right clicks are allowed. Default in game is 4 */ public final Setting<Integer> rightClickSpeed = new Setting<>(4); /** * How many degrees to randomize the yaw every tick. Set to 0 to disable */ public final Setting<Double> randomLooking113 = new Setting<>(2d); /** * Block reach distance */ public final Setting<Float> blockReachDistance = new Setting<>(4.5f); /** * How many ticks between breaking a block and starting to break the next block. Default in game is 6 ticks. * Values under 1 will be clamped. The delay only applies to non-instant (1-tick) breaks. */ public final Setting<Integer> blockBreakSpeed = new Setting<>(6); /** * How many degrees to randomize the pitch and yaw every tick. Set to 0 to disable */ public final Setting<Double> randomLooking = new Setting<>(0.01d); /** * This is the big A* setting. * As long as your cost heuristic is an *underestimate*, it's guaranteed to find you the best path. * 3.5 is always an underestimate, even if you are sprinting. * If you're walking only (with allowSprint off) 4.6 is safe. * Any value below 3.5 is never worth it. It's just more computation to find the same path, guaranteed. * (specifically, it needs to be strictly slightly less than ActionCosts.WALK_ONE_BLOCK_COST, which is about 3.56) * <p> * Setting it at 3.57 or above with sprinting, or to 4.64 or above without sprinting, will result in * faster computation, at the cost of a suboptimal path. Any value above the walk / sprint cost will result * in it going straight at its goal, and not investigating alternatives, because the combined cost / heuristic * metric gets better and better with each block, instead of slightly worse. * <p> * Finding the optimal path is worth it, so it's the default. */ public final Setting<Double> costHeuristic = new Setting<>(3.563); // a bunch of obscure internal A* settings that you probably don't want to change /** * The maximum number of times it will fetch outside loaded or cached chunks before assuming that * pathing has reached the end of the known area, and should therefore stop. */ public final Setting<Integer> pathingMaxChunkBorderFetch = new Setting<>(50); /** * Set to 1.0 to effectively disable this feature * * @see <a href="https://github.com/cabaletta/baritone/issues/18">Issue #18</a> */ public final Setting<Double> backtrackCostFavoringCoefficient = new Setting<>(0.5); /** * Toggle the following 4 settings * <p> * They have a noticeable performance impact, so they default off * <p> * Specifically, building up the avoidance map on the main thread before pathing starts actually takes a noticeable * amount of time, especially when there are a lot of mobs around, and your game jitters for like 200ms while doing so */ public final Setting<Boolean> avoidance = new Setting<>(false); /** * Set to 1.0 to effectively disable this feature * <p> * Set below 1.0 to go out of your way to walk near mob spawners */ public final Setting<Double> mobSpawnerAvoidanceCoefficient = new Setting<>(2.0); /** * Distance to avoid mob spawners. */ public final Setting<Integer> mobSpawnerAvoidanceRadius = new Setting<>(16); /** * Set to 1.0 to effectively disable this feature * <p> * Set below 1.0 to go out of your way to walk near mobs */ public final Setting<Double> mobAvoidanceCoefficient = new Setting<>(1.5); /** * Distance to avoid mobs. */ public final Setting<Integer> mobAvoidanceRadius = new Setting<>(8); /** * When running a goto towards a container block (chest, ender chest, furnace, etc), * right click and open it once you arrive. */ public final Setting<Boolean> rightClickContainerOnArrival = new Setting<>(true); /** * When running a goto towards a nether portal block, walk all the way into the portal * instead of stopping one block before. */ public final Setting<Boolean> enterPortal = new Setting<>(true); /** * Don't repropagate cost improvements below 0.01 ticks. They're all just floating point inaccuracies, * and there's no point. */ public final Setting<Boolean> minimumImprovementRepropagation = new Setting<>(true); /** * After calculating a path (potentially through cached chunks), artificially cut it off to just the part that is * entirely within currently loaded chunks. Improves path safety because cached chunks are heavily simplified. * <p> * This is much safer to leave off now, and makes pathing more efficient. More explanation in the issue. * * @see <a href="https://github.com/cabaletta/baritone/issues/114">Issue #114</a> */ public final Setting<Boolean> cutoffAtLoadBoundary = new Setting<>(false); /** * If a movement's cost increases by more than this amount between calculation and execution (due to changes * in the environment / world), cancel and recalculate */ public final Setting<Double> maxCostIncrease = new Setting<>(10D); /** * Stop 5 movements before anything that made the path COST_INF. * For example, if lava has spread across the path, don't walk right up to it then recalculate, it might * still be spreading lol */ public final Setting<Integer> costVerificationLookahead = new Setting<>(5); /** * Static cutoff factor. 0.9 means cut off the last 10% of all paths, regardless of chunk load state */ public final Setting<Double> pathCutoffFactor = new Setting<>(0.9); /** * Only apply static cutoff for paths of at least this length (in terms of number of movements) */ public final Setting<Integer> pathCutoffMinimumLength = new Setting<>(30); /** * Start planning the next path once the remaining movements tick estimates sum up to less than this value */ public final Setting<Integer> planningTickLookahead = new Setting<>(150); /** * Default size of the Long2ObjectOpenHashMap used in pathing */ public final Setting<Integer> pathingMapDefaultSize = new Setting<>(1024); /** * Load factor coefficient for the Long2ObjectOpenHashMap used in pathing * <p> * Decrease for faster map operations, but higher memory usage */ public final Setting<Float> pathingMapLoadFactor = new Setting<>(0.75f); /** * How far are you allowed to fall onto solid ground (without a water bucket)? * 3 won't deal any damage. But if you just want to get down the mountain quickly and you have * Feather Falling IV, you might set it a bit higher, like 4 or 5. */ public final Setting<Integer> maxFallHeightNoWater = new Setting<>(3); /** * How far are you allowed to fall onto solid ground (with a water bucket)? * It's not that reliable, so I've set it below what would kill an unarmored player (23) */ public final Setting<Integer> maxFallHeightBucket = new Setting<>(20); /** * Is it okay to sprint through a descend followed by a diagonal? * The player overshoots the landing, but not enough to fall off. And the diagonal ensures that there isn't * lava or anything that's !canWalkInto in that space, so it's technically safe, just a little sketchy. * <p> * Note: this is *not* related to the allowDiagonalDescend setting, that is a completely different thing. */ public final Setting<Boolean> allowOvershootDiagonalDescend = new Setting<>(true); /** * If your goal is a GoalBlock in an unloaded chunk, assume it's far enough away that the Y coord * doesn't matter yet, and replace it with a GoalXZ to the same place before calculating a path. * Once a segment ends within chunk load range of the GoalBlock, it will go back to normal behavior * of considering the Y coord. The reasoning is that if your X and Z are 10,000 blocks away, * your Y coordinate's accuracy doesn't matter at all until you get much much closer. */ public final Setting<Boolean> simplifyUnloadedYCoord = new Setting<>(true); /** * Whenever a block changes, repack the whole chunk that it's in */ public final Setting<Boolean> repackOnAnyBlockChange = new Setting<>(true); /** * If a movement takes this many ticks more than its initial cost estimate, cancel it */ public final Setting<Integer> movementTimeoutTicks = new Setting<>(100); /** * Pathing ends after this amount of time, but only if a path has been found * <p> * If no valid path (length above the minimum) has been found, pathing continues up until the failure timeout */ public final Setting<Long> primaryTimeoutMS = new Setting<>(500L); /** * Pathing can never take longer than this, even if that means failing to find any path at all */ public final Setting<Long> failureTimeoutMS = new Setting<>(2000L); /** * Planning ahead while executing a segment ends after this amount of time, but only if a path has been found * <p> * If no valid path (length above the minimum) has been found, pathing continues up until the failure timeout */ public final Setting<Long> planAheadPrimaryTimeoutMS = new Setting<>(4000L); /** * Planning ahead while executing a segment can never take longer than this, even if that means failing to find any path at all */ public final Setting<Long> planAheadFailureTimeoutMS = new Setting<>(5000L); /** * For debugging, consider nodes much much slower */ public final Setting<Boolean> slowPath = new Setting<>(false); /** * Milliseconds between each node */ public final Setting<Long> slowPathTimeDelayMS = new Setting<>(100L); /** * The alternative timeout number when slowPath is on */ public final Setting<Long> slowPathTimeoutMS = new Setting<>(40000L); /** * allows baritone to save bed waypoints when interacting with beds */ public final Setting<Boolean> doBedWaypoints = new Setting<>(true); /** * allows baritone to save death waypoints */ public final Setting<Boolean> doDeathWaypoints = new Setting<>(true); /** * The big one. Download all chunks in simplified 2-bit format and save them for better very-long-distance pathing. */ public final Setting<Boolean> chunkCaching = new Setting<>(true); /** * On save, delete from RAM any cached regions that are more than 1024 blocks away from the player * <p> * Temporarily disabled * <p> * Temporarily reenabled * * @see <a href="https://github.com/cabaletta/baritone/issues/248">Issue #248</a> */ public final Setting<Boolean> pruneRegionsFromRAM = new Setting<>(true); /** * The chunk packer queue can never grow to larger than this, if it does, the oldest chunks are discarded * <p> * The newest chunks are kept, so that if you're moving in a straight line quickly then stop, your immediate render distance is still included */ public final Setting<Integer> chunkPackerQueueMaxSize = new Setting<>(2000); /** * Fill in blocks behind you */ public final Setting<Boolean> backfill = new Setting<>(false); /** * Shows popup message in the upper right corner, similarly to when you make an advancement */ public final Setting<Boolean> logAsToast = new Setting<>(false); /** * The time of how long the message in the pop-up will display * <p> * If below 1000L (1sec), it's better to disable this */ public final Setting<Long> toastTimer = new Setting<>(5000L); /** * Print all the debug messages to chat */ public final Setting<Boolean> chatDebug = new Setting<>(false); /** * Allow chat based control of Baritone. Most likely should be disabled when Baritone is imported for use in * something else */ public final Setting<Boolean> chatControl = new Setting<>(true); /** * Some clients like Impact try to force chatControl to off, so here's a second setting to do it anyway */ public final Setting<Boolean> chatControlAnyway = new Setting<>(false); /** * Render the path */ public final Setting<Boolean> renderPath = new Setting<>(true); /** * Render the path as a line instead of a frickin thingy */ public final Setting<Boolean> renderPathAsLine = new Setting<>(false); /** * Render the goal */ public final Setting<Boolean> renderGoal = new Setting<>(true); /** * Render the goal as a sick animated thingy instead of just a box * (also controls animation of GoalXZ if {@link #renderGoalXZBeacon} is enabled) */ public final Setting<Boolean> renderGoalAnimated = new Setting<>(true); /** * Render selection boxes */ public final Setting<Boolean> renderSelectionBoxes = new Setting<>(true); /** * Ignore depth when rendering the goal */ public final Setting<Boolean> renderGoalIgnoreDepth = new Setting<>(true); /** * Renders X/Z type Goals with the vanilla beacon beam effect. Combining this with * {@link #renderGoalIgnoreDepth} will cause strange render clipping. */ public final Setting<Boolean> renderGoalXZBeacon = new Setting<>(false); /** * Ignore depth when rendering the selection boxes (to break, to place, to walk into) */ public final Setting<Boolean> renderSelectionBoxesIgnoreDepth = new Setting<>(true); /** * Ignore depth when rendering the path */ public final Setting<Boolean> renderPathIgnoreDepth = new Setting<>(true); /** * Line width of the path when rendered, in pixels */ public final Setting<Float> pathRenderLineWidthPixels = new Setting<>(5F); /** * Line width of the goal when rendered, in pixels */ public final Setting<Float> goalRenderLineWidthPixels = new Setting<>(3F); /** * Start fading out the path at 20 movements ahead, and stop rendering it entirely 30 movements ahead. * Improves FPS. */ public final Setting<Boolean> fadePath = new Setting<>(false); /** * Move without having to force the client-sided rotations */ public final Setting<Boolean> freeLook = new Setting<>(true); /** * Break and place blocks without having to force the client-sided rotations. Requires {@link #freeLook}. */ public final Setting<Boolean> blockFreeLook = new Setting<>(false); /** * Automatically elytra fly without having to force the client-sided rotations. */ public final Setting<Boolean> elytraFreeLook = new Setting<>(true); /** * Forces the client-sided yaw rotation to an average of the last {@link #smoothLookTicks} of server-sided rotations. */ public final Setting<Boolean> smoothLook = new Setting<>(false); /** * Same as {@link #smoothLook} but for elytra flying. */ public final Setting<Boolean> elytraSmoothLook = new Setting<>(false); /** * The number of ticks to average across for {@link #smoothLook}; */ public final Setting<Integer> smoothLookTicks = new Setting<>(5); /** * When true, the player will remain with its existing look direction as often as possible. * Although, in some cases this can get it stuck, hence this setting to disable that behavior. */ public final Setting<Boolean> remainWithExistingLookDirection = new Setting<>(true); /** * Will cause some minor behavioral differences to ensure that Baritone works on anticheats. * <p> * At the moment this will silently set the player's rotations when using freeLook so you're not sprinting in * directions other than forward, which is picken up by more "advanced" anticheats like AAC, but not NCP. */ public final Setting<Boolean> antiCheatCompatibility = new Setting<>(true); /** * Exclusively use cached chunks for pathing * <p> * Never turn this on */ public final Setting<Boolean> pathThroughCachedOnly = new Setting<>(false); /** * Continue sprinting while in water */ public final Setting<Boolean> sprintInWater = new Setting<>(true); /** * When GetToBlockProcess or MineProcess fails to calculate a path, instead of just giving up, mark the closest instance * of that block as "unreachable" and go towards the next closest. GetToBlock expands this search to the whole "vein"; MineProcess does not. * This is because MineProcess finds individual impossible blocks (like one block in a vein that has gravel on top then lava, so it can't break) * Whereas GetToBlock should blacklist the whole "vein" if it can't get to any of them. */ public final Setting<Boolean> blacklistClosestOnFailure = new Setting<>(true); /** * 😎 Render cached chunks as semitransparent. Doesn't work with OptiFine 😭 Rarely randomly crashes, see <a href="https://github.com/cabaletta/baritone/issues/327">this issue</a>. * <p> * Can be very useful on servers with low render distance. After enabling, you may need to reload the world in order for it to have an effect * (e.g. disconnect and reconnect, enter then exit the nether, die and respawn, etc). This may literally kill your FPS and CPU because * every chunk gets recompiled twice as much as normal, since the cached version comes into range, then the normal one comes from the server for real. * <p> * Note that flowing water is cached as AVOID, which is rendered as lava. As you get closer, you may therefore see lava falls being replaced with water falls. * <p> * SOLID is rendered as stone in the overworld, netherrack in the nether, and end stone in the end */ public final Setting<Boolean> renderCachedChunks = new Setting<>(false); /** * 0.0f = not visible, fully transparent (instead of setting this to 0, turn off renderCachedChunks) * 1.0f = fully opaque */ public final Setting<Float> cachedChunksOpacity = new Setting<>(0.5f); /** * Whether or not to allow you to run Baritone commands with the prefix */ public final Setting<Boolean> prefixControl = new Setting<>(true); /** * The command prefix for chat control */ public final Setting<String> prefix = new Setting<>("#"); /** * Use a short Baritone prefix [B] instead of [Baritone] when logging to chat */ public final Setting<Boolean> shortBaritonePrefix = new Setting<>(false); /** * Use a modern message tag instead of a prefix when logging to chat */ public final Setting<Boolean> useMessageTag = new Setting<>(false); /** * Echo commands to chat when they are run */ public final Setting<Boolean> echoCommands = new Setting<>(true); /** * Censor coordinates in goals and block positions */ public final Setting<Boolean> censorCoordinates = new Setting<>(false); /** * Censor arguments to ran commands, to hide, for example, coordinates to #goal */ public final Setting<Boolean> censorRanCommands = new Setting<>(false); /** * Stop using tools just before they are going to break. */ public final Setting<Boolean> itemSaver = new Setting<>(false); /** * Durability to leave on the tool when using itemSaver */ public final Setting<Integer> itemSaverThreshold = new Setting<>(10); /** * Always prefer silk touch tools over regular tools. This will not sacrifice speed, but it will always prefer silk * touch tools over other tools of the same speed. This includes always choosing ANY silk touch tool over your hand. */ public final Setting<Boolean> preferSilkTouch = new Setting<>(false); /** * Don't stop walking forward when you need to break blocks in your way */ public final Setting<Boolean> walkWhileBreaking = new Setting<>(true); /** * When a new segment is calculated that doesn't overlap with the current one, but simply begins where the current segment ends, * splice it on and make a longer combined path. If this setting is off, any planned segment will not be spliced and will instead * be the "next path" in PathingBehavior, and will only start after this one ends. Turning this off hurts planning ahead, * because the next segment will exist even if it's very short. * * @see #planningTickLookahead */ public final Setting<Boolean> splicePath = new Setting<>(true); /** * If we are more than 300 movements into the current path, discard the oldest segments, as they are no longer useful */ public final Setting<Integer> maxPathHistoryLength = new Setting<>(300); /** * If the current path is too long, cut off this many movements from the beginning. */ public final Setting<Integer> pathHistoryCutoffAmount = new Setting<>(50); /** * Rescan for the goal once every 5 ticks. * Set to 0 to disable. */ public final Setting<Integer> mineGoalUpdateInterval = new Setting<>(5); /** * After finding this many instances of the target block in the cache, it will stop expanding outward the chunk search. */ public final Setting<Integer> maxCachedWorldScanCount = new Setting<>(10); /** * Mine will not scan for or remember more than this many target locations. * Note that the number of locations retrieved from cache is additionaly * limited by {@link #maxCachedWorldScanCount}. */ public final Setting<Integer> mineMaxOreLocationsCount = new Setting<>(64); /** * Sets the minimum y level whilst mining - set to 0 to turn off. * if world has negative y values, subtract the min world height to get the value to put here */ public final Setting<Integer> minYLevelWhileMining = new Setting<>(0); /** * Sets the maximum y level to mine ores at. */ public final Setting<Integer> maxYLevelWhileMining = new Setting<>(2031); /** * This will only allow baritone to mine exposed ores, can be used to stop ore obfuscators on servers that use them. */ public final Setting<Boolean> allowOnlyExposedOres = new Setting<>(false); /** * When allowOnlyExposedOres is enabled this is the distance around to search. * <p> * It is recommended to keep this value low, as it dramatically increases calculation times. */ public final Setting<Integer> allowOnlyExposedOresDistance = new Setting<>(1); /** * When GetToBlock or non-legit Mine doesn't know any locations for the desired block, explore randomly instead of giving up. */ public final Setting<Boolean> exploreForBlocks = new Setting<>(true); /** * While exploring the world, offset the closest unloaded chunk by this much in both axes. * <p> * This can result in more efficient loading, if you set this to the render distance. */ public final Setting<Integer> worldExploringChunkOffset = new Setting<>(0); /** * Take the 10 closest chunks, even if they aren't strictly tied for distance metric from origin. */ public final Setting<Integer> exploreChunkSetMinimumSize = new Setting<>(10); /** * Attempt to maintain Y coordinate while exploring * <p> * -1 to disable */ public final Setting<Integer> exploreMaintainY = new Setting<>(64); /** * Replant normal Crops while farming and leave cactus and sugarcane to regrow */ public final Setting<Boolean> replantCrops = new Setting<>(true); /** * Replant nether wart while farming. This setting only has an effect when replantCrops is also enabled */ public final Setting<Boolean> replantNetherWart = new Setting<>(false); /** * Farming will scan for at most this many blocks. */ public final Setting<Integer> farmMaxScanSize = new Setting<>(256); /** * When the cache scan gives less blocks than the maximum threshold (but still above zero), scan the main world too. * <p> * Only if you have a beefy CPU and automatically mine blocks that are in cache */ public final Setting<Boolean> extendCacheOnThreshold = new Setting<>(false); /** * Don't consider the next layer in builder until the current one is done */ public final Setting<Boolean> buildInLayers = new Setting<>(false); /** * false = build from bottom to top * <p> * true = build from top to bottom */ public final Setting<Boolean> layerOrder = new Setting<>(false); /** * How high should the individual layers be? */ public final Setting<Integer> layerHeight = new Setting<>(1); /** * Start building the schematic at a specific layer. * Can help on larger builds when schematic wants to break things its already built */ public final Setting<Integer> startAtLayer = new Setting<>(0); /** * If a layer is unable to be constructed, just skip it. */ public final Setting<Boolean> skipFailedLayers = new Setting<>(false); /** * Only build the selected part of schematics */ public final Setting<Boolean> buildOnlySelection = new Setting<>(false); /** * How far to move before repeating the build. 0 to disable repeating on a certain axis, 0,0,0 to disable entirely */ public final Setting<Vec3i> buildRepeat = new Setting<>(new Vec3i(0, 0, 0)); /** * How many times to buildrepeat. -1 for infinite. */ public final Setting<Integer> buildRepeatCount = new Setting<>(-1); /** * Don't notify schematics that they are moved. * e.g. replacing will replace the same spots for every repetition * Mainly for backward compatibility. */ public final Setting<Boolean> buildRepeatSneaky = new Setting<>(true); /** * Allow standing above a block while mining it, in BuilderProcess * <p> * Experimental */ public final Setting<Boolean> breakFromAbove = new Setting<>(false); /** * As well as breaking from above, set a goal to up and to the side of all blocks to break. * <p> * Never turn this on without also turning on breakFromAbove. */ public final Setting<Boolean> goalBreakFromAbove = new Setting<>(false); /** * Build in map art mode, which makes baritone only care about the top block in each column */ public final Setting<Boolean> mapArtMode = new Setting<>(false); /** * Override builder's behavior to not attempt to correct blocks that are currently water */ public final Setting<Boolean> okIfWater = new Setting<>(false); /** * The set of incorrect blocks can never grow beyond this size */ public final Setting<Integer> incorrectSize = new Setting<>(100); /** * Multiply the cost of breaking a block that's correct in the builder's schematic by this coefficient */ public final Setting<Double> breakCorrectBlockPenaltyMultiplier = new Setting<>(10d); /** * Multiply the cost of placing a block that's incorrect in the builder's schematic by this coefficient */ public final Setting<Double> placeIncorrectBlockPenaltyMultiplier = new Setting<>(2d); /** * When this setting is true, build a schematic with the highest X coordinate being the origin, instead of the lowest */ public final Setting<Boolean> schematicOrientationX = new Setting<>(false); /** * When this setting is true, build a schematic with the highest Y coordinate being the origin, instead of the lowest */ public final Setting<Boolean> schematicOrientationY = new Setting<>(false); /** * When this setting is true, build a schematic with the highest Z coordinate being the origin, instead of the lowest */ public final Setting<Boolean> schematicOrientationZ = new Setting<>(false); /** * Rotates the schematic before building it. * Possible values are * <ul> * <li> NONE - No rotation </li> * <li> CLOCKWISE_90 - Rotate 90° clockwise </li> * <li> CLOCKWISE_180 - Rotate 180° clockwise </li> * <li> COUNTERCLOCKWISE_90 - Rotate 270° clockwise </li> * </ul> */ public final Setting<Rotation> buildSchematicRotation = new Setting<>(Rotation.NONE); /** * Mirrors the schematic before building it. * Possible values are * <ul> * <li> FRONT_BACK - mirror the schematic along its local x axis </li> * <li> LEFT_RIGHT - mirror the schematic along its local z axis </li> * </ul> */ public final Setting<Mirror> buildSchematicMirror = new Setting<>(Mirror.NONE); /** * The fallback used by the build command when no extension is specified. This may be useful if schematics of a * particular format are used often, and the user does not wish to have to specify the extension with every usage. */ public final Setting<String> schematicFallbackExtension = new Setting<>("schematic"); /** * Distance to scan every tick for updates. Expanding this beyond player reach distance (i.e. setting it to 6 or above) * is only necessary in very large schematics where rescanning the whole thing is costly. */ public final Setting<Integer> builderTickScanRadius = new Setting<>(5); /** * While mining, should it also consider dropped items of the correct type as a pathing destination (as well as ore blocks)? */ public final Setting<Boolean> mineScanDroppedItems = new Setting<>(true); /** * While mining, wait this number of milliseconds after mining an ore to see if it will drop an item * instead of immediately going onto the next one * <p> * Thanks Louca */ public final Setting<Long> mineDropLoiterDurationMSThanksLouca = new Setting<>(250L); /** * Trim incorrect positions too far away, helps performance but hurts reliability in very large schematics */ public final Setting<Boolean> distanceTrim = new Setting<>(true); /** * Cancel the current path if the goal has changed, and the path originally ended in the goal but doesn't anymore. * <p> * Currently only runs when either MineBehavior or FollowBehavior is active. * <p> * For example, if Baritone is doing "mine iron_ore", the instant it breaks the ore (and it becomes air), that location * is no longer a goal. This means that if this setting is true, it will stop there. If this setting were off, it would * continue with its path, and walk into that location. The tradeoff is if this setting is true, it mines ores much faster * since it doesn't waste any time getting into locations that no longer contain ores, but on the other hand, it misses * some drops, and continues on without ever picking them up. * <p> * Also on cosmic prisons this should be set to true since you don't actually mine the ore it just gets replaced with stone. */ public final Setting<Boolean> cancelOnGoalInvalidation = new Setting<>(true); /** * The "axis" command (aka GoalAxis) will go to a axis, or diagonal axis, at this Y level. */ public final Setting<Integer> axisHeight = new Setting<>(120); /** * Disconnect from the server upon arriving at your goal */ public final Setting<Boolean> disconnectOnArrival = new Setting<>(false); /** * Disallow MineBehavior from using X-Ray to see where the ores are. Turn this option on to force it to mine "legit" * where it will only mine an ore once it can actually see it, so it won't do or know anything that a normal player * couldn't. If you don't want it to look like you're X-Raying, turn this on * This will always explore, regardless of exploreForBlocks */ public final Setting<Boolean> legitMine = new Setting<>(false); /** * What Y level to go to for legit strip mining */ public final Setting<Integer> legitMineYLevel = new Setting<>(-59); /** * Magically see ores that are separated diagonally from existing ores. Basically like mining around the ores that it finds * in case there's one there touching it diagonally, except it checks it un-legit-ly without having the mine blocks to see it. * You can decide whether this looks plausible or not. * <p> * This is disabled because it results in some weird behavior. For example, it can """see""" the top block of a vein of iron_ore * through a lava lake. This isn't an issue normally since it won't consider anything touching lava, so it just ignores it. * However, this setting expands that and allows it to see the entire vein so it'll mine under the lava lake to get the iron that * it can reach without mining blocks adjacent to lava. This really defeats the purpose of legitMine since a player could never * do that lol, so thats one reason why its disabled */ public final Setting<Boolean> legitMineIncludeDiagonals = new Setting<>(false); /** * When mining block of a certain type, try to mine two at once instead of one. * If the block above is also a goal block, set GoalBlock instead of GoalTwoBlocks * If the block below is also a goal block, set GoalBlock to the position one down instead of GoalTwoBlocks */ public final Setting<Boolean> forceInternalMining = new Setting<>(true); /** * Modification to the previous setting, only has effect if forceInternalMining is true * If true, only apply the previous setting if the block adjacent to the goal isn't air. */ public final Setting<Boolean> internalMiningAirException = new Setting<>(true); /** * The actual GoalNear is set this distance away from the entity you're following * <p> * For example, set followOffsetDistance to 5 and followRadius to 0 to always stay precisely 5 blocks north of your follow target. */ public final Setting<Double> followOffsetDistance = new Setting<>(0D); /** * The actual GoalNear is set in this direction from the entity you're following. This value is in degrees. */ public final Setting<Float> followOffsetDirection = new Setting<>(0F); /** * The radius (for the GoalNear) of how close to your target position you actually have to be */ public final Setting<Integer> followRadius = new Setting<>(3); /** * The maximum distance to the entity you're following */ public final Setting<Integer> followTargetMaxDistance = new Setting<>(0); /** * Turn this on if your exploration filter is enormous, you don't want it to check if it's done, * and you are just fine with it just hanging on completion */ public final Setting<Boolean> disableCompletionCheck = new Setting<>(false); /** * Cached chunks (regardless of if they're in RAM or saved to disk) expire and are deleted after this number of seconds * -1 to disable * <p> * I would highly suggest leaving this setting disabled (-1). * <p> * The only valid reason I can think of enable this setting is if you are extremely low on disk space and you play on multiplayer, * and can't take (average) 300kb saved for every 512x512 area. (note that more complicated terrain is less compressible and will take more space) * <p> * However, simply discarding old chunks because they are old is inadvisable. Baritone is extremely good at correcting * itself and its paths as it learns new information, as new chunks load. There is no scenario in which having an * incorrect cache can cause Baritone to get stuck, take damage, or perform any action it wouldn't otherwise, everything * is rechecked once the real chunk is in range. * <p> * Having a robust cache greatly improves long distance pathfinding, as it's able to go around large scale obstacles * before they're in render distance. In fact, when the chunkCaching setting is disabled and Baritone starts anew * every time, or when you enter a completely new and very complicated area, it backtracks far more often because it * has to build up that cache from scratch. But after it's gone through an area just once, the next time will have zero * backtracking, since the entire area is now known and cached. */ public final Setting<Long> cachedChunksExpirySeconds = new Setting<>(-1L); /** * The function that is called when Baritone will log to chat. This function can be added to * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting * {@link Setting#value}; */ @JavaOnly public final Setting<Consumer<Component>> logger = new Setting<>((msg) -> { try { final GuiMessageTag tag = useMessageTag.value ? Helper.MESSAGE_TAG : null; Minecraft.getInstance().gui.getChat().addMessage(msg, null, tag); } catch (Throwable t) { LOGGER.warn("Failed to log message to chat: " + msg.getString(), t); } }); /** * The function that is called when Baritone will send a desktop notification. This function can be added to * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting * {@link Setting#value}; */ @JavaOnly public final Setting<BiConsumer<String, Boolean>> notifier = new Setting<>(NotificationHelper::notify); /** * The function that is called when Baritone will show a toast. This function can be added to * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting * {@link Setting#value}; */ @JavaOnly public final Setting<BiConsumer<Component, Component>> toaster = new Setting<>(BaritoneToast::addOrUpdate); /** * Print out ALL command exceptions as a stack trace to stdout, even simple syntax errors */ public final Setting<Boolean> verboseCommandExceptions = new Setting<>(false); /** * The size of the box that is rendered when the current goal is a GoalYLevel */ public final Setting<Double> yLevelBoxSize = new Setting<>(15D); /** * The color of the current path */ public final Setting<Color> colorCurrentPath = new Setting<>(Color.RED); /** * The color of the next path */ public final Setting<Color> colorNextPath = new Setting<>(Color.MAGENTA); /** * The color of the blocks to break */ public final Setting<Color> colorBlocksToBreak = new Setting<>(Color.RED); /** * The color of the blocks to place */ public final Setting<Color> colorBlocksToPlace = new Setting<>(Color.GREEN); /** * The color of the blocks to walk into */ public final Setting<Color> colorBlocksToWalkInto = new Setting<>(Color.MAGENTA); /** * The color of the best path so far */ public final Setting<Color> colorBestPathSoFar = new Setting<>(Color.BLUE); /** * The color of the path to the most recent considered node */ public final Setting<Color> colorMostRecentConsidered = new Setting<>(Color.CYAN); /** * The color of the goal box */ public final Setting<Color> colorGoalBox = new Setting<>(Color.GREEN); /** * The color of the goal box when it's inverted */ public final Setting<Color> colorInvertedGoalBox = new Setting<>(Color.RED); /** * The color of all selections */ public final Setting<Color> colorSelection = new Setting<>(Color.CYAN); /** * The color of the selection pos 1 */ public final Setting<Color> colorSelectionPos1 = new Setting<>(Color.BLACK); /** * The color of the selection pos 2 */ public final Setting<Color> colorSelectionPos2 = new Setting<>(Color.ORANGE); /** * The opacity of the selection. 0 is completely transparent, 1 is completely opaque */ public final Setting<Float> selectionOpacity = new Setting<>(.5f); /** * Line width of the goal when rendered, in pixels */ public final Setting<Float> selectionLineWidth = new Setting<>(2F); /** * Render selections */ public final Setting<Boolean> renderSelection = new Setting<>(true); /** * Ignore depth when rendering selections */ public final Setting<Boolean> renderSelectionIgnoreDepth = new Setting<>(true); /** * Render selection corners */ public final Setting<Boolean> renderSelectionCorners = new Setting<>(true); /** * Use sword to mine. */ public final Setting<Boolean> useSwordToMine = new Setting<>(true); /** * Desktop notifications */ public final Setting<Boolean> desktopNotifications = new Setting<>(false); /** * Desktop notification on path complete */ public final Setting<Boolean> notificationOnPathComplete = new Setting<>(true); /** * Desktop notification on farm fail */ public final Setting<Boolean> notificationOnFarmFail = new Setting<>(true); /** * Desktop notification on build finished */ public final Setting<Boolean> notificationOnBuildFinished = new Setting<>(true); /** * Desktop notification on explore finished */ public final Setting<Boolean> notificationOnExploreFinished = new Setting<>(true); /** * Desktop notification on mine fail */ public final Setting<Boolean> notificationOnMineFail = new Setting<>(true); /** * The number of ticks of elytra movement to simulate while firework boost is not active. Higher values are * computationally more expensive. */ public final Setting<Integer> elytraSimulationTicks = new Setting<>(20); /** * The maximum allowed deviation in pitch from a direct line-of-sight to the flight target. Higher values are * computationally more expensive. */ public final Setting<Integer> elytraPitchRange = new Setting<>(25); /** * The minimum speed that the player can drop to (in blocks/tick) before a firework is automatically deployed. */ public final Setting<Double> elytraFireworkSpeed = new Setting<>(1.2); /** * The delay after the player's position is set-back by the server that a firework may be automatically deployed. * Value is in ticks. */ public final Setting<Integer> elytraFireworkSetbackUseDelay = new Setting<>(15); /** * The minimum padding value that is added to the player's hitbox when considering which point to fly to on the * path. High values can result in points not being considered which are otherwise safe to fly to. Low values can * result in flight paths which are extremely tight, and there's the possibility of crashing due to getting too low * to the ground. */ public final Setting<Double> elytraMinimumAvoidance = new Setting<>(0.2); /** * If enabled, avoids using fireworks when descending along the flight path. */ public final Setting<Boolean> elytraConserveFireworks = new Setting<>(false); /** * Renders the raytraces that are performed by the elytra fly calculation. */ public final Setting<Boolean> elytraRenderRaytraces = new Setting<>(false); /** * Renders the raytraces that are used in the hitbox part of the elytra fly calculation. * Requires {@link #elytraRenderRaytraces}. */ public final Setting<Boolean> elytraRenderHitboxRaytraces = new Setting<>(false); /** * Renders the best elytra flight path that was simulated each tick. */ public final Setting<Boolean> elytraRenderSimulation = new Setting<>(true); /** * Automatically path to and jump off of ledges to initiate elytra flight when grounded. */ public final Setting<Boolean> elytraAutoJump = new Setting<>(false); /** * The seed used to generate chunks for long distance elytra path-finding in the nether. * Defaults to 2b2t's nether seed. */ public final Setting<Long> elytraNetherSeed = new Setting<>(146008555100680L); /** * Whether nether-pathfinder should generate terrain based on {@link #elytraNetherSeed}. * If false all chunks that haven't been loaded are assumed to be air. */ public final Setting<Boolean> elytraPredictTerrain = new Setting<>(false); /** * Automatically swap the current elytra with a new one when the durability gets too low */ public final Setting<Boolean> elytraAutoSwap = new Setting<>(true); /** * The minimum durability an elytra can have before being swapped */ public final Setting<Integer> elytraMinimumDurability = new Setting<>(5); /** * The minimum fireworks before landing early for safety */ public final Setting<Integer> elytraMinFireworksBeforeLanding = new Setting<>(5); /** * Automatically land when elytra is almost out of durability, or almost out of fireworks */ public final Setting<Boolean> elytraAllowEmergencyLand = new Setting<>(true); /** * Time between culling far away chunks from the nether pathfinder chunk cache */ public final Setting<Long> elytraTimeBetweenCacheCullSecs = new Setting<>(TimeUnit.MINUTES.toSeconds(3)); /** * Maximum distance chunks can be before being culled from the nether pathfinder chunk cache */ public final Setting<Integer> elytraCacheCullDistance = new Setting<>(5000); /** * Should elytra consider nether brick a valid landing block */ public final Setting<Boolean> elytraAllowLandOnNetherFortress = new Setting<>(false); /** * Has the user read and understood the elytra terms and conditions */ public final Setting<Boolean> elytraTermsAccepted = new Setting<>(false); /** * Verbose chat logging in elytra mode */ public final Setting<Boolean> elytraChatSpam = new Setting<>(false); /** * Sneak when magma blocks are under feet */ public final Setting<Boolean> allowWalkOnMagmaBlocks = new Setting<>(false); /** * A map of lowercase setting field names to their respective setting */ public final Map<String, Setting<?>> byLowerName; /** * A list of all settings */ public final List<Setting<?>> allSettings; public final Map<Setting<?>, Type> settingTypes; public final class Setting<T> { public T value; public final T defaultValue; private String name; private boolean javaOnly; @SuppressWarnings("unchecked") private Setting(T value) { if (value == null) { throw new IllegalArgumentException("Cannot determine value type class from null"); } this.value = value; this.defaultValue = value; this.javaOnly = false; } /** * Deprecated! Please use .value directly instead * * @return the current setting value */ @Deprecated public final T get() { return value; } public final String getName() { return name; } public Class<T> getValueClass() { // noinspection unchecked return (Class<T>) TypeUtils.resolveBaseClass(getType()); } @Override public String toString() { return SettingsUtil.settingToString(this); } /** * Reset this setting to its default value */ public void reset() { value = defaultValue; } public final Type getType() { return settingTypes.get(this); } /** * This should always be the same as whether the setting can be parsed from or serialized to a string; in other * words, the only way to modify it is by writing to {@link #value} programatically. * * @return {@code true} if the setting can not be set or read by the user */ public boolean isJavaOnly() { return javaOnly; } } /** * Marks a {@link Setting} field as being {@link Setting#isJavaOnly() Java-only} */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) private @interface JavaOnly {} // here be dragons Settings() { Field[] temp = getClass().getFields(); Map<String, Setting<?>> tmpByName = new HashMap<>(); List<Setting<?>> tmpAll = new ArrayList<>(); Map<Setting<?>, Type> tmpSettingTypes = new HashMap<>(); try { for (Field field : temp) { if (field.getType().equals(Setting.class)) { Setting<?> setting = (Setting<?>) field.get(this); String name = field.getName(); setting.name = name; setting.javaOnly = field.isAnnotationPresent(JavaOnly.class); name = name.toLowerCase(); if (tmpByName.containsKey(name)) { throw new IllegalStateException("Duplicate setting name"); } tmpByName.put(name, setting); tmpAll.add(setting); tmpSettingTypes.put(setting, ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]); } } } catch (IllegalAccessException e) { throw new IllegalStateException(e); } byLowerName = Collections.unmodifiableMap(tmpByName); allSettings = Collections.unmodifiableList(tmpAll); settingTypes = Collections.unmodifiableMap(tmpSettingTypes); } @SuppressWarnings("unchecked") public <T> List<Setting<T>> getAllValuesByType(Class<T> cla$$) { List<Setting<T>> result = new ArrayList<>(); for (Setting<?> setting : allSettings) { if (setting.getValueClass().equals(cla$$)) { result.add((Setting<T>) setting); } } return result; } } ================================================ FILE: src/api/java/baritone/api/behavior/IBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.behavior; import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.event.listener.IGameEventListener; /** * A behavior is simply a type that is able to listen to events. * * @author Brady * @see IGameEventListener * @since 9/23/2018 */ public interface IBehavior extends AbstractGameEventListener {} ================================================ FILE: src/api/java/baritone/api/behavior/ILookBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.behavior; import baritone.api.Settings; import baritone.api.behavior.look.IAimProcessor; import baritone.api.utils.Rotation; /** * @author Brady * @since 9/23/2018 */ public interface ILookBehavior extends IBehavior { /** * Updates the current {@link ILookBehavior} target to target the specified rotations on the next tick. If any sort * of block interaction is required, {@code blockInteract} should be {@code true}. It is not guaranteed that the * rotations set by the caller will be the exact rotations expressed by the client (This is due to settings like * {@link Settings#randomLooking}). If the rotations produced by this behavior are required, then the * {@link #getAimProcessor() aim processor} should be used. * * @param rotation The target rotations * @param blockInteract Whether the target rotations are needed for a block interaction */ void updateTarget(Rotation rotation, boolean blockInteract); /** * The aim processor instance for this {@link ILookBehavior}, which is responsible for applying additional, * deterministic transformations to the target rotation set by {@link #updateTarget}. * * @return The aim processor * @see IAimProcessor#fork */ IAimProcessor getAimProcessor(); } ================================================ FILE: src/api/java/baritone/api/behavior/IPathingBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.behavior; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.calc.IPathFinder; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.path.IPathExecutor; import java.util.Optional; /** * @author Brady * @since 9/23/2018 */ public interface IPathingBehavior extends IBehavior { /** * Returns the estimated remaining ticks in the current pathing * segment. Given that the return type is an optional, {@link Optional#empty()} * will be returned in the case that there is no current segment being pathed. * * @return The estimated remaining ticks in the current segment. */ default Optional<Double> ticksRemainingInSegment() { return ticksRemainingInSegment(true); } /** * Returns the estimated remaining ticks in the current pathing * segment. Given that the return type is an optional, {@link Optional#empty()} * will be returned in the case that there is no current segment being pathed. * * @param includeCurrentMovement whether or not to include the entirety of the cost of the currently executing movement in the total * @return The estimated remaining ticks in the current segment. */ default Optional<Double> ticksRemainingInSegment(boolean includeCurrentMovement) { IPathExecutor current = getCurrent(); if (current == null) { return Optional.empty(); } int start = includeCurrentMovement ? current.getPosition() : current.getPosition() + 1; return Optional.of(current.getPath().ticksRemainingFrom(start)); } /** * Returns the estimated remaining ticks to the current goal. * Given that the return type is an optional, {@link Optional#empty()} * will be returned in the case that there is no current goal. * * @return The estimated remaining ticks to the current goal. */ Optional<Double> estimatedTicksToGoal(); /** * @return The current pathing goal */ Goal getGoal(); /** * @return Whether or not a path is currently being executed. This will be false if there's currently a pause. * @see #hasPath() */ boolean isPathing(); /** * @return If there is a current path. Note that the path is not necessarily being executed, for example when there * is a pause in effect. * @see #isPathing() */ default boolean hasPath() { return getCurrent() != null; } /** * Cancels the pathing behavior or the current path calculation, and all processes that could be controlling path. * <p> * Basically, "MAKE IT STOP". * * @return Whether or not the pathing behavior was canceled. All processes are guaranteed to be canceled, but the * PathingBehavior might be in the middle of an uncancelable action like a parkour jump */ boolean cancelEverything(); /** * PLEASE never call this * <p> * If cancelEverything was like "kill" this is "sudo kill -9". Or shutting off your computer. */ void forceCancel(); /** * Returns the current path, from the current path executor, if there is one. * * @return The current path */ default Optional<IPath> getPath() { return Optional.ofNullable(getCurrent()).map(IPathExecutor::getPath); } /** * @return The current path finder being executed */ Optional<? extends IPathFinder> getInProgress(); /** * @return The current path executor */ IPathExecutor getCurrent(); /** * Returns the next path executor, created when planning ahead. * * @return The next path executor */ IPathExecutor getNext(); } ================================================ FILE: src/api/java/baritone/api/behavior/look/IAimProcessor.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.behavior.look; import baritone.api.utils.Rotation; /** * @author Brady */ public interface IAimProcessor { /** * Returns the actual rotation that will be used when the desired rotation is requested. The returned rotation * always reflects what would happen in the upcoming tick. In other words, it is a pure function, and no internal * state changes. If simulation of the rotation states beyond the next tick is required, then a * {@link IAimProcessor#fork fork} should be created. * * @param desired The desired rotation to set * @return The actual rotation */ Rotation peekRotation(Rotation desired); /** * Returns a copy of this {@link IAimProcessor} which has its own internal state and is manually tickable. * * @return The forked processor * @see ITickableAimProcessor */ ITickableAimProcessor fork(); } ================================================ FILE: src/api/java/baritone/api/behavior/look/ITickableAimProcessor.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.behavior.look; import baritone.api.utils.Rotation; /** * @author Brady */ public interface ITickableAimProcessor extends IAimProcessor { /** * Advances the internal state of this aim processor by a single tick. */ void tick(); /** * Calls {@link #tick()} the specified number of times. * * @param ticks The number of calls */ void advance(int ticks); /** * Returns the actual rotation as provided by {@link #peekRotation(Rotation)}, and then automatically advances the * internal state by one {@link #tick() tick}. * * @param rotation The desired rotation to set * @return The actual rotation */ Rotation nextRotation(Rotation rotation); } ================================================ FILE: src/api/java/baritone/api/cache/IBlockTypeAccess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; /** * @author Brady * @since 8/4/2018 */ public interface IBlockTypeAccess { BlockState getBlock(int x, int y, int z); default BlockState getBlock(BlockPos pos) { return getBlock(pos.getX(), pos.getY(), pos.getZ()); } } ================================================ FILE: src/api/java/baritone/api/cache/ICachedRegion.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; /** * @author Brady * @since 9/24/2018 */ public interface ICachedRegion extends IBlockTypeAccess { /** * Returns whether or not the block at the specified X and Z coordinates * is cached in this world. Similar to {@link ICachedWorld#isCached(int, int)}, * however, the block coordinates should in on a scale from 0 to 511 (inclusive) * because region sizes are 512x512 blocks. * * @param blockX The block X coordinate * @param blockZ The block Z coordinate * @return Whether or not the specified XZ location is cached * @see ICachedWorld#isCached(int, int) */ boolean isCached(int blockX, int blockZ); /** * @return The X coordinate of this region */ int getX(); /** * @return The Z coordinate of this region */ int getZ(); } ================================================ FILE: src/api/java/baritone/api/cache/ICachedWorld.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; import java.util.ArrayList; import net.minecraft.core.BlockPos; import net.minecraft.world.level.chunk.LevelChunk; /** * @author Brady * @since 9/24/2018 */ public interface ICachedWorld { /** * Returns the region at the specified region coordinates * * @param regionX The region X coordinate * @param regionZ The region Z coordinate * @return The region located at the specified coordinates */ ICachedRegion getRegion(int regionX, int regionZ); /** * Queues the specified chunk for packing. This entails reading the contents * of the chunk, then packing the data into the 2-bit format, and storing that * in this cached world. * * @param chunk The chunk to pack and store */ void queueForPacking(LevelChunk chunk); /** * Returns whether or not the block at the specified X and Z coordinates * is cached in this world. * * @param blockX The block X coordinate * @param blockZ The block Z coordinate * @return Whether or not the specified XZ location is cached */ boolean isCached(int blockX, int blockZ); /** * Scans the cached chunks for location of the specified special block. The * information that is returned by this method may not be up to date, because * older cached chunks can contain data that is much more likely to have changed. * * @param block The special block to search for * @param maximum The maximum number of position results to receive * @param centerX The x block coordinate center of the search * @param centerZ The z block coordinate center of the search * @param maxRegionDistanceSq The maximum region distance, squared * @return The locations found that match the special block */ ArrayList<BlockPos> getLocationsOf(String block, int maximum, int centerX, int centerZ, int maxRegionDistanceSq); /** * Reloads all of the cached regions in this world from disk. Anything that is not saved * will be lost. This operation does not execute in a new thread by default. */ void reloadAllFromDisk(); /** * Saves all of the cached regions in this world to disk. This operation does not execute * in a new thread by default. */ void save(); } ================================================ FILE: src/api/java/baritone/api/cache/IWaypoint.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; import baritone.api.utils.BetterBlockPos; import java.util.*; /** * A marker for a position in the world. * * @author Brady * @since 9/24/2018 */ public interface IWaypoint { /** * @return The label for this waypoint */ String getName(); /** * Returns the tag for this waypoint. The tag is a category * for the waypoint in a sense, it describes the source of * the waypoint. * * @return The waypoint tag */ Tag getTag(); /** * Returns the unix epoch time in milliseconds that this waypoint * was created. This value should only be set once, when the waypoint * is initially created, and not when it is being loaded from file. * * @return The unix epoch milliseconds that this waypoint was created */ long getCreationTimestamp(); /** * Returns the actual block position of this waypoint. * * @return The block position of this waypoint */ BetterBlockPos getLocation(); enum Tag { /** * Tag indicating a position explictly marked as a home base */ HOME("home", "base"), /** * Tag indicating a position that the local player has died at */ DEATH("death"), /** * Tag indicating a bed position */ BED("bed", "spawn"), /** * Tag indicating that the waypoint was user-created */ USER("user"); /** * A list of all of the */ private static final List<Tag> TAG_LIST = Collections.unmodifiableList(Arrays.asList(Tag.values())); /** * The names for the tag, anything that the tag can be referred to as. */ public final String[] names; Tag(String... names) { this.names = names; } /** * @return A name that can be passed to {@link #getByName(String)} to retrieve this tag */ public String getName() { return names[0]; } /** * Gets a tag by one of its names. * * @param name The name to search for. * @return The tag, if found, or null. */ public static Tag getByName(String name) { for (Tag action : Tag.values()) { for (String alias : action.names) { if (alias.equalsIgnoreCase(name)) { return action; } } } return null; } /** * @return All tag names. */ public static String[] getAllNames() { Set<String> names = new HashSet<>(); for (Tag tag : Tag.values()) { names.addAll(Arrays.asList(tag.names)); } return names.toArray(new String[0]); } } } ================================================ FILE: src/api/java/baritone/api/cache/IWaypointCollection.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; import java.util.Set; /** * @author Brady * @since 9/24/2018 */ public interface IWaypointCollection { /** * Adds a waypoint to this collection * * @param waypoint The waypoint */ void addWaypoint(IWaypoint waypoint); /** * Removes a waypoint from this collection * * @param waypoint The waypoint */ void removeWaypoint(IWaypoint waypoint); /** * Gets the most recently created waypoint by the specified {@link IWaypoint.Tag} * * @param tag The tag * @return The most recently created waypoint with the specified tag */ IWaypoint getMostRecentByTag(IWaypoint.Tag tag); /** * Gets all of the waypoints that have the specified tag * * @param tag The tag * @return All of the waypoints with the specified tag * @see IWaypointCollection#getAllWaypoints() */ Set<IWaypoint> getByTag(IWaypoint.Tag tag); /** * Gets all of the waypoints in this collection, regardless of the tag. * * @return All of the waypoints in this collection * @see IWaypointCollection#getByTag(IWaypoint.Tag) */ Set<IWaypoint> getAllWaypoints(); } ================================================ FILE: src/api/java/baritone/api/cache/IWorldData.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; /** * @author Brady * @since 9/24/2018 */ public interface IWorldData { /** * Returns the cached world for this world. A cached world is a simplified format * of a regular world, intended for use on multiplayer servers where chunks are not * traditionally stored to disk, allowing for long distance pathing with minimal disk usage. * * @return The cached world for this world */ ICachedWorld getCachedWorld(); /** * @return The waypoint collection for this world */ IWaypointCollection getWaypoints(); } ================================================ FILE: src/api/java/baritone/api/cache/IWorldProvider.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; import java.util.function.Consumer; /** * @author Brady * @since 9/24/2018 */ public interface IWorldProvider { /** * Returns the data of the currently loaded world * * @return The current world data */ IWorldData getCurrentWorld(); default void ifWorldLoaded(Consumer<IWorldData> callback) { final IWorldData currentWorld = this.getCurrentWorld(); if (currentWorld != null) { callback.accept(currentWorld); } } } ================================================ FILE: src/api/java/baritone/api/cache/IWorldScanner.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.Block; /** * @author Brady * @since 10/6/2018 */ public interface IWorldScanner { /** * Scans the world, up to the specified max chunk radius, for the specified blocks. * * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon * @param filter The blocks to scan for * @param max The maximum number of blocks to scan before cutoff * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value * is negative, then this condition doesn't apply. * @param maxSearchRadius The maximum chunk search radius * @return The matching block positions */ List<BlockPos> scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius); default List<BlockPos> scanChunkRadius(IPlayerContext ctx, List<Block> filter, int max, int yLevelThreshold, int maxSearchRadius) { return scanChunkRadius(ctx, new BlockOptionalMetaLookup(filter.toArray(new Block[0])), max, yLevelThreshold, maxSearchRadius); } /** * Scans a single chunk for the specified blocks. * * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon * @param filter The blocks to scan for * @param pos The position of the target chunk * @param max The maximum number of blocks to scan before cutoff * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value * is negative, then this condition doesn't apply. * @return The matching block positions */ List<BlockPos> scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold); /** * Scans a single chunk for the specified blocks. * * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon * @param blocks The blocks to scan for * @param pos The position of the target chunk * @param max The maximum number of blocks to scan before cutoff * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value * is negative, then this condition doesn't apply. * @return The matching block positions */ default List<BlockPos> scanChunk(IPlayerContext ctx, List<Block> blocks, ChunkPos pos, int max, int yLevelThreshold) { return scanChunk(ctx, new BlockOptionalMetaLookup(blocks), pos, max, yLevelThreshold); } /** * Overload of {@link #repack(IPlayerContext, int)} where the value of the {@code range} parameter is {@code 40}. * * @param ctx The player, describing the origin * @return The amount of chunks successfully queued for repacking */ int repack(IPlayerContext ctx); /** * Queues the chunks in a square formation around the specified player, using the specified * range, which represents 1/2 the square's dimensions, where the player is in the center. * * @param ctx The player, describing the origin * @param range The range to repack * @return The amount of chunks successfully queued for repacking */ int repack(IPlayerContext ctx, int range); } ================================================ FILE: src/api/java/baritone/api/cache/Waypoint.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.cache; import baritone.api.utils.BetterBlockPos; import java.util.Date; /** * Basic implementation of {@link IWaypoint} * * @author leijurv */ public class Waypoint implements IWaypoint { private final String name; private final Tag tag; private final long creationTimestamp; private final BetterBlockPos location; public Waypoint(String name, Tag tag, BetterBlockPos location) { this(name, tag, location, System.currentTimeMillis()); } /** * Constructor called when a Waypoint is read from disk, adds the creationTimestamp * as a parameter so that it is reserved after a waypoint is wrote to the disk. * * @param name The waypoint name * @param tag The waypoint tag * @param location The waypoint location * @param creationTimestamp When the waypoint was created */ public Waypoint(String name, Tag tag, BetterBlockPos location, long creationTimestamp) { this.name = name; this.tag = tag; this.location = location; this.creationTimestamp = creationTimestamp; } @Override public int hashCode() { return name.hashCode() ^ tag.hashCode() ^ location.hashCode() ^ Long.hashCode(creationTimestamp); } @Override public String getName() { return this.name; } @Override public Tag getTag() { return this.tag; } @Override public long getCreationTimestamp() { return this.creationTimestamp; } @Override public BetterBlockPos getLocation() { return this.location; } @Override public String toString() { return String.format( "%s %s %s", name, BetterBlockPos.from(location).toString(), new Date(creationTimestamp).toString() ); } @Override public boolean equals(Object o) { if (o == null) { return false; } if (!(o instanceof IWaypoint)) { return false; } IWaypoint w = (IWaypoint) o; return name.equals(w.getName()) && tag == w.getTag() && location.equals(w.getLocation()); } } ================================================ FILE: src/api/java/baritone/api/command/Command.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command; import baritone.api.IBaritone; import baritone.api.utils.IPlayerContext; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; import java.util.stream.Stream; /** * A default implementation of {@link ICommand} which provides easy access to the * command's bound {@link IBaritone} instance, {@link IPlayerContext} and an easy * way to provide multiple valid command execution names through the default constructor. * <p> * So basically, you should use it because it provides a small amount of boilerplate, * but you're not forced to use it. * * @author LoganDark * @see ICommand */ public abstract class Command implements ICommand { protected IBaritone baritone; protected IPlayerContext ctx; /** * The names of this command. This is what you put after the command prefix. */ protected final List<String> names; /** * Creates a new Baritone control command. * * @param names The names of this command. This is what you put after the command prefix. */ protected Command(IBaritone baritone, String... names) { this.names = Collections.unmodifiableList(Stream.of(names) .map(s -> s.toLowerCase(Locale.US)) .collect(Collectors.toList())); this.baritone = baritone; this.ctx = baritone.getPlayerContext(); } @Override public final List<String> getNames() { return this.names; } } ================================================ FILE: src/api/java/baritone/api/command/IBaritoneChatControl.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command; import baritone.api.Settings; import java.util.UUID; /** * @author Brady * @since 9/26/2019 */ public interface IBaritoneChatControl { /** * In certain cases chat components need to execute commands for you. For example, the paginator automatically runs * commands when you click the forward and back arrows to show you the previous/next page. * <p> * If the prefix is changed in the meantime, then the command will go to chat. That's no good. So here's a permanent * prefix that forces a command to run, regardless of the current prefix, chat/prefix control being enabled, etc. * <p> * If used right (by both developers and users), it should be impossible to expose a command accidentally to the * server. As a rule of thumb, if you have a clickable chat component, always use this prefix. If you're suggesting * a command (a component that puts text into your text box, or something else), use {@link Settings#prefix}. */ String FORCE_COMMAND_PREFIX = String.format("<<%s>>", UUID.randomUUID().toString()); } ================================================ FILE: src/api/java/baritone/api/command/ICommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.utils.Helper; import java.util.List; import java.util.stream.Stream; /** * The base for a command. * * @author Brady * @since 10/7/2019 */ public interface ICommand extends Helper { /** * Called when this command is executed. */ void execute(String label, IArgConsumer args) throws CommandException; /** * Called when the command needs to tab complete. Return a Stream representing the entries to put in the completions * list. */ Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException; /** * @return A <b>single-line</b> string containing a short description of this command's purpose. */ String getShortDesc(); /** * @return A list of lines that will be printed by the help command when the user wishes to view them. */ List<String> getLongDesc(); /** * @return A list of the names that can be accepted to have arguments passed to this command */ List<String> getNames(); /** * @return {@code true} if this command should be hidden from the help menu */ default boolean hiddenFromHelp() { return false; } } ================================================ FILE: src/api/java/baritone/api/command/ICommandSystem.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command; import baritone.api.command.argparser.IArgParserManager; /** * @author Brady * @since 10/4/2019 */ public interface ICommandSystem { IArgParserManager getParserManager(); } ================================================ FILE: src/api/java/baritone/api/command/argparser/IArgParser.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.argparser; import baritone.api.command.argument.ICommandArgument; public interface IArgParser<T> { /** * @return the class of this parser. */ Class<T> getTarget(); /** * A stateless argument parser is just that. It takes a {@link ICommandArgument} and outputs its type. */ interface Stateless<T> extends IArgParser<T> { /** * @param arg The argument to parse. * @return What it was parsed into. * @throws RuntimeException if you want the parsing to fail. The exception will be caught and turned into an * appropriate error. */ T parseArg(ICommandArgument arg) throws Exception; } /** * A stated argument parser is similar to a stateless one. It also takes a {@link ICommandArgument}, but it also * takes a second argument that can be any type, referred to as the state. */ interface Stated<T, S> extends IArgParser<T> { Class<S> getStateType(); /** * @param arg The argument to parse. * @param state Can be anything. * @return What it was parsed into. * @throws RuntimeException if you want the parsing to fail. The exception will be caught and turned into an * appropriate error. */ T parseArg(ICommandArgument arg, S state) throws Exception; } } ================================================ FILE: src/api/java/baritone/api/command/argparser/IArgParserManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.argparser; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.registry.Registry; /** * Used to retrieve {@link IArgParser} instances from the registry, by their target class. * It can be assumed that a {@link IArgParser} exists for {@link Integer}, {@link Long}, * {@link Float}, {@link Double} and {@link Boolean}. * * @author Brady * @since 10/4/2019 */ public interface IArgParserManager { /** * @param type The type trying to be parsed * @return A parser that can parse arguments into this class, if found. */ <T> IArgParser.Stateless<T> getParserStateless(Class<T> type); /** * @param type The type trying to be parsed * @return A parser that can parse arguments into this class, if found. */ <T, S> IArgParser.Stated<T, S> getParserStated(Class<T> type, Class<S> stateKlass); /** * Attempt to parse the specified argument with a stateless {@link IArgParser} that outputs the specified class. * * @param type The type to try and parse the argument into. * @param arg The argument to parse. * @return An instance of the specified class. * @throws CommandInvalidTypeException If the parsing failed */ <T> T parseStateless(Class<T> type, ICommandArgument arg) throws CommandInvalidTypeException; /** * Attempt to parse the specified argument with a stated {@link IArgParser} that outputs the specified class. * * @param type The type to try and parse the argument into. * @param arg The argument to parse. * @param state The state to pass to the {@link IArgParser.Stated}. * @return An instance of the specified class. * @throws CommandInvalidTypeException If the parsing failed * @see IArgParser.Stated */ <T, S> T parseStated(Class<T> type, Class<S> stateKlass, ICommandArgument arg, S state) throws CommandInvalidTypeException; Registry<IArgParser> getRegistry(); } ================================================ FILE: src/api/java/baritone/api/command/argument/IArgConsumer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.argument; import baritone.api.command.ICommand; import baritone.api.command.argparser.IArgParser; import baritone.api.command.datatypes.IDatatype; import baritone.api.command.datatypes.IDatatypeFor; import baritone.api.command.datatypes.IDatatypePost; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.exception.CommandNotEnoughArgumentsException; import baritone.api.command.exception.CommandTooManyArgumentsException; import baritone.api.utils.Helper; import java.util.Deque; import java.util.LinkedList; import java.util.stream.Stream; import net.minecraft.core.Direction; /** * The {@link IArgConsumer} is how {@link ICommand}s read the arguments passed to them. This class has many benefits: * * <ul> * <li>Mutability. The whole concept of the {@link IArgConsumer}} is to let you gradually consume arguments in any way * you'd like. You can change your consumption based on earlier arguments, for subcommands for example.</li> * <li>You don't need to keep track of your consumption. The {@link IArgConsumer}} keeps track of the arguments you * consume so that it can throw detailed exceptions whenever something is out of the ordinary. Additionally, if you * need to retrieve an argument after you've already consumed it - look no further than {@link #consumed()}!</li> * <li>Easy retrieval of many different types. If you need to retrieve an instance of an int or float for example, * look no further than {@link #getAs(Class)}. If you need a more powerful way of retrieving data, try out the many * {@code getDatatype...} methods.</li> * <li>It's very easy to throw detailed exceptions. The {@link IArgConsumer}} has many different methods that can * enforce the number of arguments, the type of arguments, and more, throwing different types of * {@link CommandException}s if something seems off. You're recommended to do all validation and store all needed * data in variables BEFORE logging any data to chat via {@link Helper#logDirect(String)}, so that the error * handlers can do their job and log the error to chat.</li> * </ul> */ public interface IArgConsumer { LinkedList<ICommandArgument> getArgs(); Deque<ICommandArgument> getConsumed(); /** * @param num The number of arguments to check for * @return {@code true} if there are <i>at least</i> {@code num} arguments left in this {@link IArgConsumer}} * @see #hasAny() * @see #hasAtMost(int) * @see #hasExactly(int) */ boolean has(int num); /** * @return {@code true} if there is <i>at least</i> 1 argument left in this {@link IArgConsumer}} * @see #has(int) * @see #hasAtMostOne() * @see #hasExactlyOne() */ boolean hasAny(); /** * @param num The number of arguments to check for * @return {@code true} if there are <i>at most</i> {@code num} arguments left in this {@link IArgConsumer}} * @see #has(int) * @see #hasAtMost(int) * @see #hasExactly(int) */ boolean hasAtMost(int num); /** * @return {@code true} if there is <i>at most</i> 1 argument left in this {@link IArgConsumer}} * @see #hasAny() * @see #hasAtMostOne() * @see #hasExactlyOne() */ boolean hasAtMostOne(); /** * @param num The number of arguments to check for * @return {@code true} if there are <i>exactly</i> {@code num} arguments left in this {@link IArgConsumer}} * @see #has(int) * @see #hasAtMost(int) */ boolean hasExactly(int num); /** * @return {@code true} if there is <i>exactly</i> 1 argument left in this {@link IArgConsumer}} * @see #hasAny() * @see #hasAtMostOne() */ boolean hasExactlyOne(); /** * @param index The index to peek * @return The argument at index {@code index} in this {@link IArgConsumer}}, with 0 being the next one. This does not * mutate the {@link IArgConsumer}} * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left * @see #peek() * @see #peekString(int) * @see #peekAs(Class, int) * @see #get() */ ICommandArgument peek(int index) throws CommandNotEnoughArgumentsException; /** * @return The next argument in this {@link IArgConsumer}}. This does not mutate the {@link IArgConsumer}} * @throws CommandNotEnoughArgumentsException If there is less than one argument left * @see #peek(int) * @see #peekString() * @see #peekAs(Class) * @see #get() */ ICommandArgument peek() throws CommandNotEnoughArgumentsException; /** * @param index The index to peek * @param type The type to check for * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next * argument * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left * @see #peek() * @see #getAs(Class) */ boolean is(Class<?> type, int index) throws CommandNotEnoughArgumentsException; /** * @param type The type to check for * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next * argument * @throws CommandNotEnoughArgumentsException If there is less than one argument left * @see #peek() * @see #getAs(Class) */ boolean is(Class<?> type) throws CommandNotEnoughArgumentsException; /** * @param index The index to peek * @return The value of the argument at index {@code index} in this {@link IArgConsumer}}, with 0 being the next one * This does not mutate the {@link IArgConsumer}} * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left * @see #peek() * @see #peekString() */ String peekString(int index) throws CommandNotEnoughArgumentsException; /** * @return The value of the next argument in this {@link IArgConsumer}}. This does not mutate the {@link IArgConsumer}} * @throws CommandNotEnoughArgumentsException If there is less than one argument left * @see #peekString(int) * @see #getString() */ String peekString() throws CommandNotEnoughArgumentsException; /** * @param index The index to peek * @param enumClass The class to search * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the * next argument's value * @throws java.util.NoSuchElementException If the constant couldn't be found * @see #peekEnumOrNull(Class) * @see #getEnum(Class) * @see ICommandArgument#getEnum(Class) */ <E extends Enum<?>> E peekEnum(Class<E> enumClass, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; /** * @param enumClass The class to search * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the * next argument's value * @throws CommandInvalidTypeException If the constant couldn't be found * @see #peekEnumOrNull(Class) * @see #getEnum(Class) * @see ICommandArgument#getEnum(Class) */ <E extends Enum<?>> E peekEnum(Class<E> enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; /** * @param index The index to peek * @param enumClass The class to search * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the * next argument's value. If no constant could be found, null * @see #peekEnum(Class) * @see #getEnumOrNull(Class) * @see ICommandArgument#getEnum(Class) */ <E extends Enum<?>> E peekEnumOrNull(Class<E> enumClass, int index) throws CommandNotEnoughArgumentsException; /** * @param enumClass The class to search * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the * next argument's value. If no constant could be found, null * @see #peekEnum(Class) * @see #getEnumOrNull(Class) * @see ICommandArgument#getEnum(Class) */ <E extends Enum<?>> E peekEnumOrNull(Class<E> enumClass) throws CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the argument at the specified index into the specified * class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @param index The index to peek * @return An instance of the specified type * @throws CommandInvalidTypeException If the parsing failed * @see IArgParser * @see #peekAs(Class) * @see #peekAsOrDefault(Class, Object, int) * @see #peekAsOrNull(Class, int) */ <T> T peekAs(Class<T> type, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the next argument into the specified class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @return An instance of the specified type * @throws CommandInvalidTypeException If the parsing failed * @see IArgParser * @see #peekAs(Class, int) * @see #peekAsOrDefault(Class, Object) * @see #peekAsOrNull(Class) */ <T> T peekAs(Class<T> type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the argument at the specified index into the specified * class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @param def The value to return if the argument can't be parsed * @param index The index to peek * @return An instance of the specified type, or {@code def} if it couldn't be parsed * @see IArgParser * @see #peekAsOrDefault(Class, Object) * @see #peekAs(Class, int) * @see #peekAsOrNull(Class, int) */ <T> T peekAsOrDefault(Class<T> type, T def, int index) throws CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the next argument into the specified class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @param def The value to return if the argument can't be parsed * @return An instance of the specified type, or {@code def} if it couldn't be parsed * @see IArgParser * @see #peekAsOrDefault(Class, Object, int) * @see #peekAs(Class) * @see #peekAsOrNull(Class) */ <T> T peekAsOrDefault(Class<T> type, T def) throws CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the argument at the specified index into the specified * class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @param index The index to peek * @return An instance of the specified type, or {@code null} if it couldn't be parsed * @see IArgParser * @see #peekAsOrNull(Class) * @see #peekAs(Class, int) * @see #peekAsOrDefault(Class, Object, int) */ <T> T peekAsOrNull(Class<T> type, int index) throws CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the next argument into the specified class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @return An instance of the specified type, or {@code null} if it couldn't be parsed * @see IArgParser * @see #peekAsOrNull(Class, int) * @see #peekAs(Class) * @see #peekAsOrDefault(Class, Object) */ <T> T peekAsOrNull(Class<T> type) throws CommandNotEnoughArgumentsException; <T> T peekDatatype(IDatatypeFor<T> datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; <T, O> T peekDatatype(IDatatypePost<T, O> datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; <T, O> T peekDatatype(IDatatypePost<T, O> datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; <T> T peekDatatypeOrNull(IDatatypeFor<T> datatype); <T, O> T peekDatatypeOrNull(IDatatypePost<T, O> datatype); <T, O, D extends IDatatypePost<T, O>> T peekDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; <T, O, D extends IDatatypePost<T, O>> T peekDatatypePostOrDefault(D datatype, O original, T def); <T, O, D extends IDatatypePost<T, O>> T peekDatatypePostOrNull(D datatype, O original); /** * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * <p> * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method. * * @param datatype The datatype to get * @return The datatype instance * @see IDatatype * @see IDatatypeFor */ <T, D extends IDatatypeFor<T>> T peekDatatypeFor(Class<D> datatype); /** * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * <p> * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method. * * @param datatype The datatype to get * @param def The default value * @return The datatype instance, or {@code def} if it throws an exception * @see IDatatype * @see IDatatypeFor */ <T, D extends IDatatypeFor<T>> T peekDatatypeForOrDefault(Class<D> datatype, T def); /** * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * <p> * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method. * * @param datatype The datatype to get * @return The datatype instance, or {@code null} if it throws an exception * @see IDatatype * @see IDatatypeFor */ <T, D extends IDatatypeFor<T>> T peekDatatypeForOrNull(Class<D> datatype); /** * Gets the next argument and returns it. This consumes the first argument so that subsequent calls will return * later arguments * * @return The next argument * @throws CommandNotEnoughArgumentsException If there's less than one argument left */ ICommandArgument get() throws CommandNotEnoughArgumentsException; /** * Gets the value of the next argument and returns it. This consumes the first argument so that subsequent calls * will return later arguments * * @return The value of the next argument * @throws CommandNotEnoughArgumentsException If there's less than one argument left */ String getString() throws CommandNotEnoughArgumentsException; /** * Gets an enum value from the enum class with the same name as the next argument's value * <p> * For example if you getEnum as an {@link Direction}, and the next argument's value is "up", this will return * {@link Direction#UP} * * @param enumClass The enum class to search * @return An enum constant of that class with the same name as the next argument's value * @throws CommandInvalidTypeException If the constant couldn't be found * @see #peekEnum(Class) * @see #getEnumOrNull(Class) * @see ICommandArgument#getEnum(Class) */ <E extends Enum<?>> E getEnum(Class<E> enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; /** * Gets an enum value from the enum class with the same name as the next argument's value * <p> * For example if you getEnum as an {@link Direction}, and the next argument's value is "up", this will return * {@link Direction#UP} * * @param enumClass The enum class to search * @param def The default value * @return An enum constant of that class with the same name as the next argument's value, or {@code def} if it * couldn't be found * @see #getEnum(Class) * @see #getEnumOrNull(Class) * @see #peekEnumOrNull(Class) * @see ICommandArgument#getEnum(Class) */ <E extends Enum<?>> E getEnumOrDefault(Class<E> enumClass, E def) throws CommandNotEnoughArgumentsException; /** * Gets an enum value from the enum class with the same name as the next argument's value * <p> * For example if you getEnum as an {@link Direction}, and the next argument's value is "up", this will return * {@link Direction#UP} * * @param enumClass The enum class to search * @return An enum constant of that class with the same name as the next argument's value, or {@code null} if it * couldn't be found * @see #getEnum(Class) * @see #getEnumOrDefault(Class, Enum) * @see #peekEnumOrNull(Class) * @see ICommandArgument#getEnum(Class) */ <E extends Enum<?>> E getEnumOrNull(Class<E> enumClass) throws CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the next argument into the specified class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @return An instance of the specified type * @throws CommandInvalidTypeException If the parsing failed * @see IArgParser * @see #get() * @see #getAsOrDefault(Class, Object) * @see #getAsOrNull(Class) * @see #peekAs(Class) * @see #peekAsOrDefault(Class, Object, int) * @see #peekAsOrNull(Class, int) */ <T> T getAs(Class<T> type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the next argument into the specified class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @param def The default value * @return An instance of the specified type, or {@code def} if it couldn't be parsed * @see IArgParser * @see #get() * @see #getAs(Class) * @see #getAsOrNull(Class) * @see #peekAs(Class) * @see #peekAsOrDefault(Class, Object, int) * @see #peekAsOrNull(Class, int) */ <T> T getAsOrDefault(Class<T> type, T def) throws CommandNotEnoughArgumentsException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse the next argument into the specified class * <p> * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire * {@link IArgConsumer}}. * * @param type The type to peek as * @return An instance of the specified type, or {@code null} if it couldn't be parsed * @see IArgParser * @see #get() * @see #getAs(Class) * @see #getAsOrDefault(Class, Object) * @see #peekAs(Class) * @see #peekAsOrDefault(Class, Object, int) * @see #peekAsOrNull(Class, int) */ <T> T getAsOrNull(Class<T> type) throws CommandNotEnoughArgumentsException; <T, O, D extends IDatatypePost<T, O>> T getDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; <T, O, D extends IDatatypePost<T, O>> T getDatatypePostOrDefault(D datatype, O original, T _default); <T, O, D extends IDatatypePost<T, O>> T getDatatypePostOrNull(D datatype, O original); <T, D extends IDatatypeFor<T>> T getDatatypeFor(D datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; <T, D extends IDatatypeFor<T>> T getDatatypeForOrDefault(D datatype, T def); <T, D extends IDatatypeFor<T>> T getDatatypeForOrNull(D datatype); <T extends IDatatype> Stream<String> tabCompleteDatatype(T datatype); /** * Returns the "raw rest" of the string. For example, from a string <code>arg1 arg2  arg3</code>, split * into three {@link ICommandArgument}s {@code "arg1"}, {@code "arg2"}, and {@code "arg3"}: * * <ul> * <li>{@code rawRest()} would return <code>arg1 arg2  arg3</code></li> * <li>After calling {@link #get()}, {@code rawRest()} would return <code>arg2  arg3</code> (note the * double space - it is preserved!)</li> * <li>After calling {@link #get()} again, {@code rawRest()} would return {@code "arg3"}</li> * <li>After calling {@link #get()} one last time, {@code rawRest()} would return {@code ""}</li> * </ul> * * @return The "raw rest" of the string. */ String rawRest(); /** * @param min The minimum amount of arguments to require. * @throws CommandNotEnoughArgumentsException If there are less than {@code min} arguments left. * @see #requireMax(int) * @see #requireExactly(int) */ void requireMin(int min) throws CommandNotEnoughArgumentsException; /** * @param max The maximum amount of arguments allowed. * @throws CommandTooManyArgumentsException If there are more than {@code max} arguments left. * @see #requireMin(int) * @see #requireExactly(int) */ void requireMax(int max) throws CommandTooManyArgumentsException; /** * @param args The exact amount of arguments to require. * @throws CommandNotEnoughArgumentsException If there are less than {@code args} arguments left. * @throws CommandTooManyArgumentsException If there are more than {@code args} arguments left. * @see #requireMin(int) * @see #requireMax(int) */ void requireExactly(int args) throws CommandException; /** * @return If this {@link IArgConsumer}} has consumed at least one argument. * @see #consumed() * @see #consumedString() */ boolean hasConsumed(); /** * @return The last argument this {@link IArgConsumer}} has consumed, or an "unknown" argument, indicated by a * comamnd argument index that has a value of {@code -1}, if no arguments have been consumed yet. * @see #consumedString() * @see #hasConsumed() */ ICommandArgument consumed(); /** * @return The value of thelast argument this {@link IArgConsumer}} has consumed, or an empty string if no arguments * have been consumed yet * @see #consumed() * @see #hasConsumed() */ String consumedString(); /** * @return A copy of this {@link IArgConsumer}}. It has the same arguments (both consumed and not), but does not * affect or mutate this instance. Useful for the various {@code peek} functions */ IArgConsumer copy(); } ================================================ FILE: src/api/java/baritone/api/command/argument/ICommandArgument.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.argument; import baritone.api.command.argparser.IArgParser; import baritone.api.command.exception.CommandInvalidTypeException; import net.minecraft.core.Direction; /** * A {@link ICommandArgument} is an immutable object representing one command argument. It contains data on the index of * that argument, its value, and the rest of the string that argument was found in * <p> * You're recommended to use {@link IArgConsumer}}s to handle these. * * @author Brady * @since 10/2/2019 */ public interface ICommandArgument { /** * @return The index of this command argument in the list of command arguments generated */ int getIndex(); /** * @return The raw value of just this argument */ String getValue(); /** * @return The raw value of the remaining arguments after this one was captured */ String getRawRest(); /** * Gets an enum value from the enum class with the same name as this argument's value * <p> * For example if you getEnum as an {@link Direction}, and this argument's value is "up", it will return {@link * Direction#UP} * * @param enumClass The enum class to search * @return An enum constant of that class with the same name as this argument's value * @throws CommandInvalidTypeException If the constant couldn't be found * @see IArgConsumer#peekEnum(Class) * @see IArgConsumer#peekEnum(Class, int) * @see IArgConsumer#peekEnumOrNull(Class) * @see IArgConsumer#peekEnumOrNull(Class, int) * @see IArgConsumer#getEnum(Class) * @see IArgConsumer#getEnumOrNull(Class) */ <E extends Enum<?>> E getEnum(Class<E> enumClass) throws CommandInvalidTypeException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse this argument into the specified class * * @param type The class to parse this argument into * @return An instance of the specified type * @throws CommandInvalidTypeException If the parsing failed */ <T> T getAs(Class<T> type) throws CommandInvalidTypeException; /** * Tries to use a <b>stateless</b> {@link IArgParser} to parse this argument into the specified class * * @param type The class to parse this argument into * @return If the parser succeeded */ <T> boolean is(Class<T> type); /** * Tries to use a <b>stated</b> {@link IArgParser} to parse this argument into the specified class * * @param type The class to parse this argument into * @return An instance of the specified type * @throws CommandInvalidTypeException If the parsing failed */ <T, S> T getAs(Class<T> type, Class<S> stateType, S state) throws CommandInvalidTypeException; /** * Tries to use a <b>stated</b> {@link IArgParser} to parse this argument into the specified class * * @param type The class to parse this argument into * @return If the parser succeeded */ <T, S> boolean is(Class<T> type, Class<S> stateType, S state); } ================================================ FILE: src/api/java/baritone/api/command/datatypes/BlockById.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import java.util.stream.Stream; public enum BlockById implements IDatatypeFor<Block> { INSTANCE; @Override public Block get(IDatatypeContext ctx) throws CommandException { ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString()); Block block; if ((block = BuiltInRegistries.BLOCK.getOptional(id).orElse(null)) == null) { throw new IllegalArgumentException("no block found by that id"); } return block; } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { String arg = ctx.getConsumer().getString(); return new TabCompleteHelper() .append( BuiltInRegistries.BLOCK.keySet() .stream() .map(Object::toString) ) .filterPrefixNamespaced(arg) .sortAlphabetically() .stream(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/EntityClassById.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EntityType; import java.util.stream.Stream; public enum EntityClassById implements IDatatypeFor<EntityType> { INSTANCE; @Override public EntityType get(IDatatypeContext ctx) throws CommandException { ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString()); EntityType entity; if ((entity = BuiltInRegistries.ENTITY_TYPE.getOptional(id).orElse(null)) == null) { throw new IllegalArgumentException("no entity found by that id"); } return entity; } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { return new TabCompleteHelper() .append(BuiltInRegistries.ENTITY_TYPE.stream().map(Object::toString)) .filterPrefixNamespaced(ctx.getConsumer().getString()) .sortAlphabetically() .stream(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/ForAxis.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import net.minecraft.core.Direction; import java.util.Locale; import java.util.stream.Stream; public enum ForAxis implements IDatatypeFor<Direction.Axis> { INSTANCE; @Override public Direction.Axis get(IDatatypeContext ctx) throws CommandException { return Direction.Axis.valueOf(ctx.getConsumer().getString().toUpperCase(Locale.US)); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { return new TabCompleteHelper() .append(Stream.of(Direction.Axis.values()) .map(Direction.Axis::getName).map(String::toLowerCase)) .filterPrefix(ctx.getConsumer().getString()) .stream(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.utils.BlockOptionalMeta; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.properties.Property; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; public enum ForBlockOptionalMeta implements IDatatypeFor<BlockOptionalMeta> { INSTANCE; /** * Matches (domain:)?name([(property=value)*])? but the input can be truncated at any position. * domain and name are [a-z0-9_.-]+ and [a-z0-9/_.-]+ because that's what mc 1.13+ accepts. * property and value use the same format as domain. */ // Good luck reading this. private static Pattern PATTERN = Pattern.compile("(?:[a-z0-9_.-]+:)?(?:[a-z0-9/_.-]+(?:\\[(?:(?:[a-z0-9_.-]+=[a-z0-9_.-]+,)*(?:[a-z0-9_.-]+(?:=(?:[a-z0-9_.-]+(?:\\])?)?)?)?|\\])?)?)?"); @Override public BlockOptionalMeta get(IDatatypeContext ctx) throws CommandException { return new BlockOptionalMeta(ctx.getConsumer().getString()); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { String arg = ctx.getConsumer().peekString(); if (!PATTERN.matcher(arg).matches()) { // Invalid format; we can't complete this. ctx.getConsumer().getString(); return Stream.empty(); } if (arg.endsWith("]")) { // We are already done. ctx.getConsumer().getString(); return Stream.empty(); } if (!arg.contains("[")) { // no properties so we are completing the block id return ctx.getConsumer().tabCompleteDatatype(BlockById.INSTANCE); } ctx.getConsumer().getString(); // destructuring assignment? Please? String blockId, properties; { String[] parts = splitLast(arg, '['); blockId = parts[0]; properties = parts[1]; } Block block = BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(blockId)).orElse(null); if (block == null) { // This block doesn't exist so there's no properties to complete. return Stream.empty(); } String leadingProperties, lastProperty; { String[] parts = splitLast(properties, ','); leadingProperties = parts[0]; lastProperty = parts[1]; } if (!lastProperty.contains("=")) { // The last property-value pair doesn't have a value yet so we are completing its name Set<String> usedProps = Stream.of(leadingProperties.split(",")) .map(pair -> pair.split("=")[0]) .collect(Collectors.toSet()); String prefix = arg.substring(0, arg.length() - lastProperty.length()); return new TabCompleteHelper() .append( block.getStateDefinition() .getProperties() .stream() .map(Property::getName) ) .filter(prop -> !usedProps.contains(prop)) .filterPrefix(lastProperty) .sortAlphabetically() .map(prop -> prefix + prop) .stream(); } String lastName, lastValue; { String[] parts = splitLast(lastProperty, '='); lastName = parts[0]; lastValue = parts[1]; } // We are completing the value of a property String prefix = arg.substring(0, arg.length() - lastValue.length()); Property<?> property = block.getStateDefinition().getProperty(lastName); if (property == null) { // The property does not exist so there's no values to complete return Stream.empty(); } return new TabCompleteHelper() .append(getValues(property)) .filterPrefix(lastValue) .sortAlphabetically() .map(val -> prefix + val) .stream(); } /** * Always returns exactly two strings. * If the separator is not found the FIRST returned string is empty. */ private static String[] splitLast(String string, char chr) { int idx = string.lastIndexOf(chr); if (idx == -1) { return new String[]{"", string}; } return new String[]{string.substring(0, idx), string.substring(idx + 1)}; } // this shouldn't need to be a separate method? private static <T extends Comparable<T>> Stream<String> getValues(Property<T> property) { return property.getPossibleValues().stream().map(property::getName); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/ForDirection.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import java.util.Locale; import java.util.stream.Stream; import net.minecraft.core.Direction; public enum ForDirection implements IDatatypeFor<Direction> { INSTANCE; @Override public Direction get(IDatatypeContext ctx) throws CommandException { return Direction.valueOf(ctx.getConsumer().getString().toUpperCase(Locale.US)); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { return new TabCompleteHelper() .append(Stream.of(Direction.values()) .map(Direction::getName).map(String::toLowerCase)) .filterPrefix(ctx.getConsumer().getString()) .stream(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/ForWaypoints.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.IBaritone; import baritone.api.cache.IWaypoint; import baritone.api.cache.IWaypointCollection; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import java.util.Comparator; import java.util.stream.Stream; public enum ForWaypoints implements IDatatypeFor<IWaypoint[]> { INSTANCE; @Override public IWaypoint[] get(IDatatypeContext ctx) throws CommandException { final String input = ctx.getConsumer().getString(); final IWaypoint.Tag tag = IWaypoint.Tag.getByName(input); // If the input doesn't resolve to a valid tag, resolve by name return tag == null ? getWaypointsByName(ctx.getBaritone(), input) : getWaypointsByTag(ctx.getBaritone(), tag); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { return new TabCompleteHelper() .append(getWaypointNames(ctx.getBaritone())) .sortAlphabetically() .prepend(IWaypoint.Tag.getAllNames()) .filterPrefix(ctx.getConsumer().getString()) .stream(); } public static IWaypointCollection waypoints(IBaritone baritone) { return baritone.getWorldProvider().getCurrentWorld().getWaypoints(); } public static IWaypoint[] getWaypoints(IBaritone baritone) { return waypoints(baritone).getAllWaypoints().stream() .sorted(Comparator.comparingLong(IWaypoint::getCreationTimestamp).reversed()) .toArray(IWaypoint[]::new); } public static String[] getWaypointNames(IBaritone baritone) { return Stream.of(getWaypoints(baritone)) .map(IWaypoint::getName) .filter(name -> !name.isEmpty()) .toArray(String[]::new); } public static IWaypoint[] getWaypointsByTag(IBaritone baritone, IWaypoint.Tag tag) { return waypoints(baritone).getByTag(tag).stream() .sorted(Comparator.comparingLong(IWaypoint::getCreationTimestamp).reversed()) .toArray(IWaypoint[]::new); } public static IWaypoint[] getWaypointsByName(IBaritone baritone, String name) { return Stream.of(getWaypoints(baritone)) .filter(waypoint -> waypoint.getName().equalsIgnoreCase(name)) .toArray(IWaypoint[]::new); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/IDatatype.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argparser.IArgParser; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.stream.Stream; /** * An {@link IDatatype} is similar to an {@link IArgParser} in the sense that it is capable of consuming an argument * to transform it into a usable form as the code desires. * <p> * A fundamental difference is that an {@link IDatatype} is capable of consuming multiple arguments. For example, * {@link RelativeBlockPos} is an {@link IDatatypePost} which requires at least 3 {@link RelativeCoordinate} arguments * to be specified. * <p> * Another difference is that an {@link IDatatype} can be tab-completed, providing comprehensive auto completion * that can substitute based on existing input or provide possibilities for the next piece of input. * * @see IDatatypeContext * @see IDatatypeFor * @see IDatatypePost */ public interface IDatatype { /** * Attempts to complete missing or partial input provided through the {@link IArgConsumer}} provided by * {@link IDatatypeContext#getConsumer()} in order to aide the user in executing commands. * <p> * One benefit over datatypes over {@link IArgParser}s is that instead of each command trying to guess what values * the datatype will accept, or simply not tab completing at all, datatypes that support tab completion can provide * accurate information using the same methods used to parse arguments in the first place. * * @param ctx The argument consumer to tab complete * @return A stream representing the strings that can be tab completed. DO NOT INCLUDE SPACES IN ANY STRINGS. * @see IArgConsumer#tabCompleteDatatype(IDatatype) */ Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException; } ================================================ FILE: src/api/java/baritone/api/command/datatypes/IDatatypeContext.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.IBaritone; import baritone.api.command.argument.IArgConsumer; /** * Provides an {@link IDatatype} with contextual information so * that it can perform the desired operation on the target level. * * @author Brady * @see IDatatype * @since 9/26/2019 */ public interface IDatatypeContext { /** * Provides the {@link IBaritone} instance that is associated with the action relating to datatype handling. * * @return The context {@link IBaritone} instance. */ IBaritone getBaritone(); /** * Provides the {@link IArgConsumer}} to fetch input information from. * * @return The context {@link IArgConsumer}}. */ IArgConsumer getConsumer(); } ================================================ FILE: src/api/java/baritone/api/command/datatypes/IDatatypeFor.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import java.util.function.Supplier; /** * An {@link IDatatype} which acts as a {@link Supplier}, in essence. The only difference * is that it requires an {@link IDatatypeContext} to be provided due to the expectation that * implementations of {@link IDatatype} are singletons. */ public interface IDatatypeFor<T> extends IDatatype { /** * Consumes the desired amount of arguments from the specified {@link IDatatypeContext}, and * then returns the parsed value. This method will more than likely return a {@link IllegalArgumentException} * if the expected input does not conform to a parseable value. As far as a {@link CommandException} being * thrown is concerned, see the note below for specifics. * * @param ctx The context * @return The parsed data-type * @throws CommandException If there was an issue parsing using another type or arguments could not be polled. * @see IDatatypeContext */ T get(IDatatypeContext ctx) throws CommandException; } ================================================ FILE: src/api/java/baritone/api/command/datatypes/IDatatypePost.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import java.util.function.Function; /** * An {@link IDatatype} which acts as a {@link Function}, in essence. The only difference * is that it requires an {@link IDatatypeContext} to be provided due to the expectation that * implementations of {@link IDatatype} are singletons. */ public interface IDatatypePost<T, O> extends IDatatype { /** * Takes the expected input and transforms it based on the value held by {@code original}. If {@code original} * is null, it is expected that the implementation of this method has a case to handle it, such that a * {@link NullPointerException} will never be thrown as a result. * * @param ctx The datatype context * @param original The transformable value * @return The transformed value */ T apply(IDatatypeContext ctx, O original) throws CommandException; } ================================================ FILE: src/api/java/baritone/api/command/datatypes/IDatatypePostFunction.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; /** * @author Brady * @since 9/26/2019 */ public interface IDatatypePostFunction<T, O> { T apply(O original) throws CommandException; } ================================================ FILE: src/api/java/baritone/api/command/datatypes/ItemById.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import java.util.stream.Stream; public enum ItemById implements IDatatypeFor<Item> { INSTANCE; @Override public Item get(IDatatypeContext ctx) throws CommandException { ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString()); Item item; if ((item = BuiltInRegistries.ITEM.getOptional(id).orElse(null)) == null) { throw new IllegalArgumentException("No item found by that id"); } return item; } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { return new TabCompleteHelper() .append( BuiltInRegistries.BLOCK.keySet() .stream() .map(ResourceLocation::toString) ) .filterPrefixNamespaced(ctx.getConsumer().getString()) .sortAlphabetically() .stream(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/NearbyPlayer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.IBaritone; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import java.util.List; import java.util.stream.Stream; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Player; /** * An {@link IDatatype} used to resolve nearby players, those within * render distance of the target {@link IBaritone} instance. */ public enum NearbyPlayer implements IDatatypeFor<Player> { INSTANCE; @Override public Player get(IDatatypeContext ctx) throws CommandException { final String username = ctx.getConsumer().getString(); return getPlayers(ctx).stream() .filter(s -> s.getName().getString().equalsIgnoreCase(username)) .findFirst().orElse(null); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { return new TabCompleteHelper() .append(getPlayers(ctx).stream().map(Player::getName).map(Component::getString)) .filterPrefix(ctx.getConsumer().getString()) .sortAlphabetically() .stream(); } private static List<? extends Player> getPlayers(IDatatypeContext ctx) { return ctx.getBaritone().getPlayerContext().world().players(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/RelativeBlockPos.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.utils.BetterBlockPos; import java.util.stream.Stream; public enum RelativeBlockPos implements IDatatypePost<BetterBlockPos, BetterBlockPos> { INSTANCE; @Override public BetterBlockPos apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { if (origin == null) { origin = BetterBlockPos.ORIGIN; } final IArgConsumer consumer = ctx.getConsumer(); return new BetterBlockPos( consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x), consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y), consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.z) ); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { final IArgConsumer consumer = ctx.getConsumer(); if (consumer.hasAny() && !consumer.has(4)) { while (consumer.has(2)) { if (consumer.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) { break; } consumer.get(); } return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); } return Stream.empty(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; public enum RelativeCoordinate implements IDatatypePost<Double, Double> { INSTANCE; private static String ScalesAliasRegex = "[kKmM]"; private static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(" + ScalesAliasRegex + "?)|)$"); @Override public Double apply(IDatatypeContext ctx, Double origin) throws CommandException { if (origin == null) { origin = 0.0D; } Matcher matcher = PATTERN.matcher(ctx.getConsumer().getString()); if (!matcher.matches()) { throw new IllegalArgumentException("pattern doesn't match"); } boolean isRelative = !matcher.group(1).isEmpty(); double offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2).replaceAll(ScalesAliasRegex, "")); if (matcher.group(2).toLowerCase().contains("k")) { offset *= 1000; } if (matcher.group(2).toLowerCase().contains("m")) { offset *= 1000000; } if (isRelative) { return origin + offset; } return offset; } @Override public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException { final IArgConsumer consumer = ctx.getConsumer(); if (!consumer.has(2) && consumer.getString().matches("^(~|$)")) { return Stream.of("~"); } return Stream.empty(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/RelativeFile.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.utils.Helper; import net.minecraft.client.Minecraft; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.FileSystems; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.Locale; import java.util.Objects; import java.util.stream.Stream; public enum RelativeFile implements IDatatypePost<File, File> { INSTANCE; @Override public File apply(IDatatypeContext ctx, File original) throws CommandException { if (original == null) { original = new File("./"); } Path path; try { path = FileSystems.getDefault().getPath(ctx.getConsumer().getString()); } catch (InvalidPathException e) { throw new IllegalArgumentException("invalid path"); } return getCanonicalFileUnchecked(original.toPath().resolve(path).toFile()); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) { return Stream.empty(); } /** * Seriously * * @param file File * @return Canonical file of file * @author LoganDark */ private static File getCanonicalFileUnchecked(File file) { try { return file.getCanonicalFile(); } catch (IOException e) { throw new UncheckedIOException(e); } } public static Stream<String> tabComplete(IArgConsumer consumer, File base0) throws CommandException { // I will not make the caller deal with this, seriously // Tab complete code is beautiful and I'm not going to bloat it with dumb ass checked exception bullshit -LoganDark // lol owned -Brady File base = getCanonicalFileUnchecked(base0); String currentPathStringThing = consumer.getString(); Path currentPath = FileSystems.getDefault().getPath(currentPathStringThing); Path basePath = currentPath.isAbsolute() ? currentPath.getRoot() : base.toPath(); boolean useParent = !currentPathStringThing.isEmpty() && !currentPathStringThing.endsWith(File.separator); File currentFile = currentPath.isAbsolute() ? currentPath.toFile() : new File(base, currentPathStringThing); return Stream.of(Objects.requireNonNull(getCanonicalFileUnchecked( useParent ? currentFile.getParentFile() : currentFile ).listFiles())) .map(f -> (currentPath.isAbsolute() ? f : basePath.relativize(f.toPath()).toString()) + (f.isDirectory() ? File.separator : "")) .filter(s -> s.toLowerCase(Locale.US).startsWith(currentPathStringThing.toLowerCase(Locale.US))) .filter(s -> !s.contains(" ")); } @Deprecated public static File gameDir() { return gameDir(Helper.mc); } public static File gameDir(Minecraft mc) { File gameDir = mc.gameDirectory.getAbsoluteFile(); if (gameDir.getName().equals(".")) { return gameDir.getParentFile(); } return gameDir; } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/RelativeGoal.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.pathing.goals.GoalXZ; import baritone.api.pathing.goals.GoalYLevel; import baritone.api.utils.BetterBlockPos; import java.util.stream.Stream; public enum RelativeGoal implements IDatatypePost<Goal, BetterBlockPos> { INSTANCE; @Override public Goal apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { if (origin == null) { origin = BetterBlockPos.ORIGIN; } final IArgConsumer consumer = ctx.getConsumer(); GoalBlock goalBlock = consumer.peekDatatypePostOrNull(RelativeGoalBlock.INSTANCE, origin); if (goalBlock != null) { return goalBlock; } GoalXZ goalXZ = consumer.peekDatatypePostOrNull(RelativeGoalXZ.INSTANCE, origin); if (goalXZ != null) { return goalXZ; } GoalYLevel goalYLevel = consumer.peekDatatypePostOrNull(RelativeGoalYLevel.INSTANCE, origin); if (goalYLevel != null) { return goalYLevel; } // when the user doesn't input anything, default to the origin return new GoalBlock(origin); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) { return ctx.getConsumer().tabCompleteDatatype(RelativeCoordinate.INSTANCE); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; import java.util.stream.Stream; import net.minecraft.util.Mth; public enum RelativeGoalBlock implements IDatatypePost<GoalBlock, BetterBlockPos> { INSTANCE; @Override public GoalBlock apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { if (origin == null) { origin = BetterBlockPos.ORIGIN; } final IArgConsumer consumer = ctx.getConsumer(); return new GoalBlock( Mth.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x)), Mth.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y)), Mth.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.z)) ); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) { final IArgConsumer consumer = ctx.getConsumer(); if (consumer.hasAtMost(3)) { return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); } return Stream.empty(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalXZ; import baritone.api.utils.BetterBlockPos; import java.util.stream.Stream; import net.minecraft.util.Mth; public enum RelativeGoalXZ implements IDatatypePost<GoalXZ, BetterBlockPos> { INSTANCE; @Override public GoalXZ apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { if (origin == null) { origin = BetterBlockPos.ORIGIN; } final IArgConsumer consumer = ctx.getConsumer(); return new GoalXZ( Mth.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x)), Mth.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.z)) ); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) { final IArgConsumer consumer = ctx.getConsumer(); if (consumer.hasAtMost(2)) { return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); } return Stream.empty(); } } ================================================ FILE: src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.datatypes; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalYLevel; import baritone.api.utils.BetterBlockPos; import java.util.stream.Stream; import net.minecraft.util.Mth; public enum RelativeGoalYLevel implements IDatatypePost<GoalYLevel, BetterBlockPos> { INSTANCE; @Override public GoalYLevel apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { if (origin == null) { origin = BetterBlockPos.ORIGIN; } return new GoalYLevel( Mth.floor(ctx.getConsumer().getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y)) ); } @Override public Stream<String> tabComplete(IDatatypeContext ctx) { final IArgConsumer consumer = ctx.getConsumer(); if (consumer.hasAtMost(1)) { return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); } return Stream.empty(); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandErrorMessageException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; public abstract class CommandErrorMessageException extends CommandException { protected CommandErrorMessageException(String reason) { super(reason); } protected CommandErrorMessageException(String reason, Throwable cause) { super(reason, cause); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; public abstract class CommandException extends Exception implements ICommandException { protected CommandException(String reason) { super(reason); } protected CommandException(String reason, Throwable cause) { super(reason, cause); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandInvalidArgumentException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; import baritone.api.command.argument.ICommandArgument; public abstract class CommandInvalidArgumentException extends CommandErrorMessageException { public final ICommandArgument arg; protected CommandInvalidArgumentException(ICommandArgument arg, String message) { super(formatMessage(arg, message)); this.arg = arg; } protected CommandInvalidArgumentException(ICommandArgument arg, String message, Throwable cause) { super(formatMessage(arg, message), cause); this.arg = arg; } private static String formatMessage(ICommandArgument arg, String message) { return String.format( "Error at argument #%s: %s", arg.getIndex() == -1 ? "<unknown>" : Integer.toString(arg.getIndex() + 1), message ); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandInvalidStateException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; public class CommandInvalidStateException extends CommandErrorMessageException { public CommandInvalidStateException(String reason) { super(reason); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandInvalidTypeException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; import baritone.api.command.argument.ICommandArgument; public class CommandInvalidTypeException extends CommandInvalidArgumentException { public CommandInvalidTypeException(ICommandArgument arg, String expected) { super(arg, String.format("Expected %s", expected)); } public CommandInvalidTypeException(ICommandArgument arg, String expected, Throwable cause) { super(arg, String.format("Expected %s", expected), cause); } public CommandInvalidTypeException(ICommandArgument arg, String expected, String got) { super(arg, String.format("Expected %s, but got %s instead", expected, got)); } public CommandInvalidTypeException(ICommandArgument arg, String expected, String got, Throwable cause) { super(arg, String.format("Expected %s, but got %s instead", expected, got), cause); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandNoParserForTypeException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; public class CommandNoParserForTypeException extends CommandUnhandledException { public CommandNoParserForTypeException(Class<?> klass) { super(String.format("Could not find a handler for type %s", klass.getSimpleName())); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandNotEnoughArgumentsException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; public class CommandNotEnoughArgumentsException extends CommandErrorMessageException { public CommandNotEnoughArgumentsException(int minArgs) { super(String.format("Not enough arguments (expected at least %d)", minArgs)); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandNotFoundException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; import java.util.List; import static baritone.api.utils.Helper.HELPER; public class CommandNotFoundException extends CommandException { public final String command; public CommandNotFoundException(String command) { super(String.format("Command not found: %s", command)); this.command = command; } @Override public void handle(ICommand command, List<ICommandArgument> args) { HELPER.logDirect(getMessage()); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandTooManyArgumentsException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; public class CommandTooManyArgumentsException extends CommandErrorMessageException { public CommandTooManyArgumentsException(int maxArgs) { super(String.format("Too many arguments (expected at most %d)", maxArgs)); } } ================================================ FILE: src/api/java/baritone/api/command/exception/CommandUnhandledException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; import java.util.List; import static baritone.api.utils.Helper.HELPER; public class CommandUnhandledException extends RuntimeException implements ICommandException { public CommandUnhandledException(String message) { super(message); } public CommandUnhandledException(Throwable cause) { super(cause); } @Override public void handle(ICommand command, List<ICommandArgument> args) { HELPER.logUnhandledException(this); } } ================================================ FILE: src/api/java/baritone/api/command/exception/ICommandException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.exception; import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; import java.util.List; import net.minecraft.ChatFormatting; import static baritone.api.utils.Helper.HELPER; /** * The base for a Baritone Command Exception, checked or unchecked. Provides a * {@link #handle(ICommand, List)} method that is used to provide useful output * to the user for diagnosing issues that may have occurred during execution. * <p> * Anything implementing this interface should be assignable to {@link Exception}. * * @author Brady * @since 9/20/2019 */ public interface ICommandException { /** * @return The exception details * @see Exception#getMessage() */ String getMessage(); /** * Called when this exception is thrown, to handle the exception. * * @param command The command that threw it. * @param args The arguments the command was called with. */ default void handle(ICommand command, List<ICommandArgument> args) { HELPER.logDirect(this.getMessage(), ChatFormatting.RED); } } ================================================ FILE: src/api/java/baritone/api/command/helpers/Paginator.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.helpers; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.utils.Helper; import java.awt.*; import java.util.Arrays; import java.util.List; import java.util.function.Function; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; public class Paginator<E> implements Helper { public final List<E> entries; public int pageSize = 8; public int page = 1; public Paginator(List<E> entries) { this.entries = entries; } public Paginator(E... entries) { this.entries = Arrays.asList(entries); } public Paginator<E> setPageSize(int pageSize) { this.pageSize = pageSize; return this; } public int getMaxPage() { return (entries.size() - 1) / pageSize + 1; } public boolean validPage(int page) { return page > 0 && page <= getMaxPage(); } public Paginator<E> skipPages(int pages) { page += pages; return this; } public void display(Function<E, Component> transform, String commandPrefix) { int offset = (page - 1) * pageSize; for (int i = offset; i < offset + pageSize; i++) { if (i < entries.size()) { logDirect(transform.apply(entries.get(i))); } else { logDirect("--", ChatFormatting.DARK_GRAY); } } boolean hasPrevPage = commandPrefix != null && validPage(page - 1); boolean hasNextPage = commandPrefix != null && validPage(page + 1); MutableComponent prevPageComponent = Component.literal("<<"); if (hasPrevPage) { prevPageComponent.setStyle(prevPageComponent.getStyle() .withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format("%s %d", commandPrefix, page - 1) )) .withHoverEvent(new HoverEvent( HoverEvent.Action.SHOW_TEXT, Component.literal("Click to view previous page") ))); } else { prevPageComponent.setStyle(prevPageComponent.getStyle().withColor(ChatFormatting.DARK_GRAY)); } MutableComponent nextPageComponent = Component.literal(">>"); if (hasNextPage) { nextPageComponent.setStyle(nextPageComponent.getStyle() .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("%s %d", commandPrefix, page + 1))) .withHoverEvent(new HoverEvent( HoverEvent.Action.SHOW_TEXT, Component.literal("Click to view next page") ))); } else { nextPageComponent.setStyle(nextPageComponent.getStyle().withColor(ChatFormatting.DARK_GRAY)); } MutableComponent pagerComponent = Component.literal(""); pagerComponent.setStyle(pagerComponent.getStyle().withColor(ChatFormatting.GRAY)); pagerComponent.append(prevPageComponent); pagerComponent.append(" | "); pagerComponent.append(nextPageComponent); pagerComponent.append(String.format(" %d/%d", page, getMaxPage())); logDirect(pagerComponent); } public void display(Function<E, Component> transform) { display(transform, null); } public static <T> void paginate(IArgConsumer consumer, Paginator<T> pagi, Runnable pre, Function<T, Component> transform, String commandPrefix) throws CommandException { int page = 1; consumer.requireMax(1); if (consumer.hasAny()) { page = consumer.getAs(Integer.class); if (!pagi.validPage(page)) { throw new CommandInvalidTypeException( consumer.consumed(), String.format( "a valid page (1-%d)", pagi.getMaxPage() ), consumer.consumed().getValue() ); } } pagi.skipPages(page - pagi.page); if (pre != null) { pre.run(); } pagi.display(transform, commandPrefix); } public static <T> void paginate(IArgConsumer consumer, List<T> elems, Runnable pre, Function<T, Component> transform, String commandPrefix) throws CommandException { paginate(consumer, new Paginator<>(elems), pre, transform, commandPrefix); } public static <T> void paginate(IArgConsumer consumer, T[] elems, Runnable pre, Function<T, Component> transform, String commandPrefix) throws CommandException { paginate(consumer, Arrays.asList(elems), pre, transform, commandPrefix); } public static <T> void paginate(IArgConsumer consumer, Paginator<T> pagi, Function<T, Component> transform, String commandPrefix) throws CommandException { paginate(consumer, pagi, null, transform, commandPrefix); } public static <T> void paginate(IArgConsumer consumer, List<T> elems, Function<T, Component> transform, String commandPrefix) throws CommandException { paginate(consumer, new Paginator<>(elems), null, transform, commandPrefix); } public static <T> void paginate(IArgConsumer consumer, T[] elems, Function<T, Component> transform, String commandPrefix) throws CommandException { paginate(consumer, Arrays.asList(elems), null, transform, commandPrefix); } public static <T> void paginate(IArgConsumer consumer, Paginator<T> pagi, Runnable pre, Function<T, Component> transform) throws CommandException { paginate(consumer, pagi, pre, transform, null); } public static <T> void paginate(IArgConsumer consumer, List<T> elems, Runnable pre, Function<T, Component> transform) throws CommandException { paginate(consumer, new Paginator<>(elems), pre, transform, null); } public static <T> void paginate(IArgConsumer consumer, T[] elems, Runnable pre, Function<T, Component> transform) throws CommandException { paginate(consumer, Arrays.asList(elems), pre, transform, null); } public static <T> void paginate(IArgConsumer consumer, Paginator<T> pagi, Function<T, Component> transform) throws CommandException { paginate(consumer, pagi, null, transform, null); } public static <T> void paginate(IArgConsumer consumer, List<T> elems, Function<T, Component> transform) throws CommandException { paginate(consumer, new Paginator<>(elems), null, transform, null); } public static <T> void paginate(IArgConsumer consumer, T[] elems, Function<T, Component> transform) throws CommandException { paginate(consumer, Arrays.asList(elems), null, transform, null); } } ================================================ FILE: src/api/java/baritone/api/command/helpers/TabCompleteHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.helpers; import baritone.api.BaritoneAPI; import baritone.api.Settings; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.manager.ICommandManager; import baritone.api.event.events.TabCompleteEvent; import baritone.api.utils.SettingsUtil; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import net.minecraft.resources.ResourceLocation; /** * The {@link TabCompleteHelper} is a <b>single-use</b> object that helps you handle tab completion. It includes helper * methods for appending and prepending streams, sorting, filtering by prefix, and so on. * <p> * The recommended way to use this class is: * <ul> * <li>Create a new instance with the empty constructor</li> * <li>Use {@code append}, {@code prepend} or {@code add<something>} methods to add completions</li> * <li>Sort using {@link #sort(Comparator)} or {@link #sortAlphabetically()} and then filter by prefix using * {@link #filterPrefix(String)}</li> * <li>Get the stream using {@link #stream()}</li> * <li>Pass it up to whatever's calling your tab complete function (i.e. * {@link ICommandManager#tabComplete(String)} or {@link IArgConsumer}#tabCompleteDatatype(IDatatype)})</li> * </ul> * <p> * For advanced users: if you're intercepting {@link TabCompleteEvent}s directly, use {@link #build()} instead for an * array. */ public class TabCompleteHelper { private Stream<String> stream; public TabCompleteHelper(String[] base) { stream = Stream.of(base); } public TabCompleteHelper(List<String> base) { stream = base.stream(); } public TabCompleteHelper() { stream = Stream.empty(); } /** * Appends the specified stream to this {@link TabCompleteHelper} and returns it for chaining * * @param source The stream to append * @return This {@link TabCompleteHelper} after having appended the stream * @see #append(String...) * @see #append(Class) */ public TabCompleteHelper append(Stream<String> source) { stream = Stream.concat(stream, source); return this; } /** * Appends the specified strings to this {@link TabCompleteHelper} and returns it for chaining * * @param source The stream to append * @return This {@link TabCompleteHelper} after having appended the strings * @see #append(Stream) * @see #append(Class) */ public TabCompleteHelper append(String... source) { return append(Stream.of(source)); } /** * Appends all values of the specified enum to this {@link TabCompleteHelper} and returns it for chaining * * @param num The enum to append the values of * @return This {@link TabCompleteHelper} after having appended the values * @see #append(Stream) * @see #append(String...) */ public TabCompleteHelper append(Class<? extends Enum<?>> num) { return append( Stream.of(num.getEnumConstants()) .map(Enum::name) .map(String::toLowerCase) ); } /** * Prepends the specified stream to this {@link TabCompleteHelper} and returns it for chaining * * @param source The stream to prepend * @return This {@link TabCompleteHelper} after having prepended the stream * @see #prepend(String...) * @see #prepend(Class) */ public TabCompleteHelper prepend(Stream<String> source) { stream = Stream.concat(source, stream); return this; } /** * Prepends the specified strings to this {@link TabCompleteHelper} and returns it for chaining * * @param source The stream to prepend * @return This {@link TabCompleteHelper} after having prepended the strings * @see #prepend(Stream) * @see #prepend(Class) */ public TabCompleteHelper prepend(String... source) { return prepend(Stream.of(source)); } /** * Prepends all values of the specified enum to this {@link TabCompleteHelper} and returns it for chaining * * @param num The enum to prepend the values of * @return This {@link TabCompleteHelper} after having prepended the values * @see #prepend(Stream) * @see #prepend(String...) */ public TabCompleteHelper prepend(Class<? extends Enum<?>> num) { return prepend( Stream.of(num.getEnumConstants()) .map(Enum::name) .map(String::toLowerCase) ); } /** * Apply the specified {@code transform} to every element <b>currently</b> in this {@link TabCompleteHelper} and * return this object for chaining * * @param transform The transform to apply * @return This {@link TabCompleteHelper} */ public TabCompleteHelper map(Function<String, String> transform) { stream = stream.map(transform); return this; } /** * Apply the specified {@code filter} to every element <b>currently</b> in this {@link TabCompleteHelper} and return * this object for chaining * * @param filter The filter to apply * @return This {@link TabCompleteHelper} */ public TabCompleteHelper filter(Predicate<String> filter) { stream = stream.filter(filter); return this; } /** * Apply the specified {@code sort} to every element <b>currently</b> in this {@link TabCompleteHelper} and return * this object for chaining * * @param comparator The comparator to use * @return This {@link TabCompleteHelper} */ public TabCompleteHelper sort(Comparator<String> comparator) { stream = stream.sorted(comparator); return this; } /** * Sort every element <b>currently</b> in this {@link TabCompleteHelper} alphabetically and return this object for * chaining * * @return This {@link TabCompleteHelper} */ public TabCompleteHelper sortAlphabetically() { return sort(String.CASE_INSENSITIVE_ORDER); } /** * Filter out any element that doesn't start with {@code prefix} and return this object for chaining * * @param prefix The prefix to filter for * @return This {@link TabCompleteHelper} */ public TabCompleteHelper filterPrefix(String prefix) { return filter(x -> x.toLowerCase(Locale.US).startsWith(prefix.toLowerCase(Locale.US))); } /** * Filter out any element that doesn't start with {@code prefix} and return this object for chaining * <p> * Assumes every element in this {@link TabCompleteHelper} is a {@link ResourceLocation} * * @param prefix The prefix to filter for * @return This {@link TabCompleteHelper} */ public TabCompleteHelper filterPrefixNamespaced(String prefix) { ResourceLocation loc = ResourceLocation.tryParse(prefix); if (loc == null) { stream = Stream.empty(); return this; } return filterPrefix(loc.toString()); } /** * @return An array containing every element in this {@link TabCompleteHelper} * @see #stream() */ public String[] build() { return stream.toArray(String[]::new); } /** * @return A stream containing every element in this {@link TabCompleteHelper} * @see #build() */ public Stream<String> stream() { return stream; } /** * Appends every command in the specified {@link ICommandManager} to this {@link TabCompleteHelper} * * @param manager A command manager * @return This {@link TabCompleteHelper} */ public TabCompleteHelper addCommands(ICommandManager manager) { return append(manager.getRegistry().descendingStream() .flatMap(command -> command.getNames().stream()) .distinct() ); } /** * Appends every setting in the {@link Settings} to this {@link TabCompleteHelper} * * @return This {@link TabCompleteHelper} */ public TabCompleteHelper addSettings() { return append( BaritoneAPI.getSettings().allSettings.stream() .filter(s -> !s.isJavaOnly()) .map(Settings.Setting::getName) .sorted(String.CASE_INSENSITIVE_ORDER) ); } /** * Appends every modified setting in the {@link Settings} to this {@link TabCompleteHelper} * * @return This {@link TabCompleteHelper} */ public TabCompleteHelper addModifiedSettings() { return append( SettingsUtil.modifiedSettings(BaritoneAPI.getSettings()).stream() .map(Settings.Setting::getName) .sorted(String.CASE_INSENSITIVE_ORDER) ); } /** * Appends every {@link Boolean} setting in the {@link Settings} to this {@link TabCompleteHelper} * * @return This {@link TabCompleteHelper} */ public TabCompleteHelper addToggleableSettings() { return append( BaritoneAPI.getSettings().getAllValuesByType(Boolean.class).stream() .map(Settings.Setting::getName) .sorted(String.CASE_INSENSITIVE_ORDER) ); } } ================================================ FILE: src/api/java/baritone/api/command/manager/ICommandManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.manager; import baritone.api.IBaritone; import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.registry.Registry; import net.minecraft.util.Tuple; import java.util.List; import java.util.stream.Stream; /** * @author Brady * @since 9/21/2019 */ public interface ICommandManager { IBaritone getBaritone(); Registry<ICommand> getRegistry(); /** * @param name The command name to search for. * @return The command, if found. */ ICommand getCommand(String name); boolean execute(String string); boolean execute(Tuple<String, List<ICommandArgument>> expanded); Stream<String> tabComplete(Tuple<String, List<ICommandArgument>> expanded); Stream<String> tabComplete(String prefix); } ================================================ FILE: src/api/java/baritone/api/command/registry/Registry.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.command.registry; import java.util.*; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * This registry class allows for registration and unregistration of a certain type. This is mainly designed for use by * event handlers where newly registered ones are encountered first during iteration and can therefore override older * ones. In Baritone, this is used for commands and argument parsers so that mods and addons can extend Baritone's * functionality without resorting to hacks, wrappers, or mixins. * * @param <V> The entry type that will be stored in this registry. This can be anything, really - preferably anything * that works as a HashMap key, as that's what's used to keep track of which entries are registered or not. */ public class Registry<V> { /** * An internal linked list of all the entries that are currently registered. This is a linked list so that entries * can be inserted at the beginning, which means that newer entries are encountered first during iteration. This is * an important property of the registry that makes it more useful than a simple list, and also the reason it does * not just use a map. */ private final Deque<V> _entries = new LinkedList<>(); /** * A HashSet containing every entry currently registered. Entries are added to this set when something is registered * and removed from the set when they are unregistered. An entry being present in this set indicates that it is * currently registered, can be removed, and should not be reregistered until it is removed. */ private final Set<V> registered = new HashSet<>(); /** * The collection of entries that are currently in this registry. This is a collection (and not a list) because, * internally, entries are stored in a linked list, which is not the same as a normal list. */ public final Collection<V> entries = Collections.unmodifiableCollection(_entries); /** * @param entry The entry to check. * @return If this entry is currently registered in this registry. */ public boolean registered(V entry) { return registered.contains(entry); } /** * Ensures that the entry {@code entry} is registered. * * @param entry The entry to register. * @return A boolean indicating whether or not this is a new registration. No matter the value of this boolean, the * entry is always guaranteed to now be in this registry. This boolean simply indicates if the entry was <i>not</i> * in the map prior to this method call. */ public boolean register(V entry) { if (!registered(entry)) { _entries.addFirst(entry); registered.add(entry); return true; } return false; } /** * Unregisters this entry from this registry. After this method call, the entry is guaranteed to be removed from the * registry, since each entry only ever appears once. * * @param entry The entry to unregister. */ public void unregister(V entry) { if (!registered(entry)) { return; } _entries.remove(entry); registered.remove(entry); } /** * Returns an iterator that iterates over each entry in this registry, with the newest elements iterated over first. * Internally, as new elements are prepended to the registry rather than appended to the end, this order is the best * way to search through the registry if you want to discover newer items first. */ public Iterator<V> iterator() { return _entries.iterator(); } /** * Returns an iterator that iterates over each entry in this registry, in the order they were added. Internally, * this iterates through the registry backwards, as new elements are prepended to the registry rather than appended * to the end. You should only do this when you need to, for example, list elements in order - it is almost always * fine to simply use {@link Iterable#forEach(Consumer) forEach} on the {@link #entries} collection instead. */ public Iterator<V> descendingIterator() { return _entries.descendingIterator(); } /** * Returns a stream that contains each entry in this registry, with the newest elements ordered first. Internally, * as new elements are prepended to the registry rather than appended to the end, this order is the best way to * search through the registry if you want to discover newer items first. */ public Stream<V> stream() { return _entries.stream(); } /** * Returns a stream that returns each entry in this registry, in the order they were added. Internally, this orders * the registry backwards, as new elements are prepended to the registry rather than appended to the end. You should * only use this when you need to, for example, list elements in order - it is almost always fine to simply use the * regular {@link #stream()} method instead. */ public Stream<V> descendingStream() { return StreamSupport.stream(Spliterators.spliterator( descendingIterator(), _entries.size(), Spliterator.SIZED | Spliterator.SUBSIZED ), false); } } ================================================ FILE: src/api/java/baritone/api/event/events/BlockChangeEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.utils.Pair; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.state.BlockState; import java.util.List; /** * @author Brady */ public final class BlockChangeEvent { private final ChunkPos chunk; private final List<Pair<BlockPos, BlockState>> blocks; public BlockChangeEvent(ChunkPos pos, List<Pair<BlockPos, BlockState>> blocks) { this.chunk = pos; this.blocks = blocks; } public ChunkPos getChunkPos() { return this.chunk; } public List<Pair<BlockPos, BlockState>> getBlocks() { return this.blocks; } } ================================================ FILE: src/api/java/baritone/api/event/events/BlockInteractEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import net.minecraft.core.BlockPos; /** * Called when the local player interacts with a block, can be either {@link Type#START_BREAK} or {@link Type#USE}. * * @author Brady * @since 8/22/2018 */ public final class BlockInteractEvent { /** * The position of the block interacted with */ private final BlockPos pos; /** * The type of interaction that occurred */ private final Type type; public BlockInteractEvent(BlockPos pos, Type type) { this.pos = pos; this.type = type; } /** * @return The position of the block interacted with */ public final BlockPos getPos() { return this.pos; } /** * @return The type of interaction with the target block */ public final Type getType() { return this.type; } public enum Type { /** * We're starting to break the target block. */ START_BREAK, /** * We're right clicking on the target block. Either placing or interacting with. */ USE } } ================================================ FILE: src/api/java/baritone/api/event/events/ChatEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.event.events.type.Cancellable; /** * @author Brady * @since 8/1/2018 */ public final class ChatEvent extends Cancellable { /** * The message being sent */ private final String message; public ChatEvent(String message) { this.message = message; } /** * @return The message being sent */ public final String getMessage() { return this.message; } } ================================================ FILE: src/api/java/baritone/api/event/events/ChunkEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.event.events.type.EventState; /** * @author Brady * @since 8/2/2018 */ public final class ChunkEvent { /** * The state of the event */ private final EventState state; /** * The type of chunk event that occurred * * @see Type */ private final Type type; /** * The Chunk X position. */ private final int x; /** * The Chunk Z position. */ private final int z; public ChunkEvent(EventState state, Type type, int x, int z) { this.state = state; this.type = type; this.x = x; this.z = z; } /** * @return The state of the event */ public EventState getState() { return this.state; } /** * @return The type of chunk event that occurred; */ public Type getType() { return this.type; } /** * @return The Chunk X position. */ public int getX() { return this.x; } /** * @return The Chunk Z position. */ public int getZ() { return this.z; } /** * @return {@code true} if the event was fired after a chunk population */ public boolean isPostPopulate() { return this.state == EventState.POST && this.type.isPopulate(); } public enum Type { /** * When the chunk is constructed. */ LOAD, /** * When the chunk is deconstructed. */ UNLOAD, /** * When the chunk is being populated with blocks, tile entities, etc. * <p> * And it's a full chunk */ POPULATE_FULL, /** * When the chunk is being populated with blocks, tile entities, etc. * <p> * And it's a partial chunk */ POPULATE_PARTIAL; public final boolean isPopulate() { return this == POPULATE_FULL || this == POPULATE_PARTIAL; } } } ================================================ FILE: src/api/java/baritone/api/event/events/PacketEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.event.events.type.EventState; import net.minecraft.network.Connection; import net.minecraft.network.protocol.Packet; /** * @author Brady * @since 8/6/2018 */ public final class PacketEvent { private final Connection networkManager; private final EventState state; private final Packet<?> packet; public PacketEvent(Connection networkManager, EventState state, Packet<?> packet) { this.networkManager = networkManager; this.state = state; this.packet = packet; } public final Connection getNetworkManager() { return this.networkManager; } public final EventState getState() { return this.state; } public final Packet<?> getPacket() { return this.packet; } @SuppressWarnings("unchecked") public final <T extends Packet<?>> T cast() { return (T) this.packet; } } ================================================ FILE: src/api/java/baritone/api/event/events/PathEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; public enum PathEvent { CALC_STARTED, CALC_FINISHED_NOW_EXECUTING, CALC_FAILED, NEXT_SEGMENT_CALC_STARTED, NEXT_SEGMENT_CALC_FINISHED, CONTINUING_ONTO_PLANNED_NEXT, SPLICING_ONTO_NEXT_EARLY, AT_GOAL, PATH_FINISHED_NEXT_STILL_CALCULATING, NEXT_CALC_FAILED, DISCARD_NEXT, CANCELED; } ================================================ FILE: src/api/java/baritone/api/event/events/PlayerUpdateEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.event.events.type.EventState; /** * @author Brady * @since 8/21/2018 */ public final class PlayerUpdateEvent { /** * The state of the event */ private final EventState state; public PlayerUpdateEvent(EventState state) { this.state = state; } /** * @return The state of the event */ public final EventState getState() { return this.state; } } ================================================ FILE: src/api/java/baritone/api/event/events/RenderEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import com.mojang.blaze3d.vertex.PoseStack; import org.joml.Matrix4f; /** * @author Brady * @since 8/5/2018 */ public final class RenderEvent { /** * The current render partial ticks */ private final float partialTicks; private final Matrix4f projectionMatrix; private final PoseStack modelViewStack; public RenderEvent(float partialTicks, PoseStack modelViewStack, Matrix4f projectionMatrix) { this.partialTicks = partialTicks; this.modelViewStack = modelViewStack; this.projectionMatrix = projectionMatrix; } /** * @return The current render partial ticks */ public final float getPartialTicks() { return this.partialTicks; } public PoseStack getModelViewStack() { return this.modelViewStack; } public Matrix4f getProjectionMatrix() { return this.projectionMatrix; } } ================================================ FILE: src/api/java/baritone/api/event/events/RotationMoveEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.utils.Rotation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.phys.Vec3; /** * @author Brady * @since 8/21/2018 */ public final class RotationMoveEvent { /** * The type of event */ private final Type type; private final Rotation original; /** * The yaw rotation */ private float yaw; /** * The pitch rotation */ private float pitch; public RotationMoveEvent(Type type, float yaw, float pitch) { this.type = type; this.original = new Rotation(yaw, pitch); this.yaw = yaw; this.pitch = pitch; } public Rotation getOriginal() { return this.original; } /** * Set the yaw movement rotation * * @param yaw Yaw rotation */ public void setYaw(float yaw) { this.yaw = yaw; } /** * @return The yaw rotation */ public float getYaw() { return this.yaw; } /** * Set the pitch movement rotation * * @param pitch Pitch rotation */ public void setPitch(float pitch) { this.pitch = pitch; } /** * @return The pitch rotation */ public float getPitch() { return pitch; } /** * @return The type of the event */ public Type getType() { return this.type; } public enum Type { /** * Called when the player's motion is updated. * * @see Entity#moveRelative(float, Vec3) */ MOTION_UPDATE, /** * Called when the player jumps. * * @see LivingEntity */ JUMP } } ================================================ FILE: src/api/java/baritone/api/event/events/SprintStateEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; /** * @author Brady * @since 1/18/2019 */ public final class SprintStateEvent { private Boolean state; public final void setState(boolean state) { this.state = state; } public final Boolean getState() { return this.state; } } ================================================ FILE: src/api/java/baritone/api/event/events/TabCompleteEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.event.events.type.Cancellable; /** * @author LoganDark */ public final class TabCompleteEvent extends Cancellable { public final String prefix; public String[] completions; public TabCompleteEvent(String prefix) { this.prefix = prefix; this.completions = null; } } ================================================ FILE: src/api/java/baritone/api/event/events/TickEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.event.events.type.EventState; import net.minecraft.client.Minecraft; import java.util.function.BiFunction; /** * Called on and after each game tick of the primary {@link Minecraft} instance and dispatched to all Baritone * instances. * <p> * When {@link #state} is {@link EventState#PRE}, the event is being called just prior to when the current in-game * screen is ticked. When {@link #state} is {@link EventState#POST}, the event is being called at the very end * of the {@link Minecraft#runTick()} method. */ public final class TickEvent { private static int overallTickCount; private final EventState state; private final Type type; private final int count; public TickEvent(EventState state, Type type, int count) { this.state = state; this.type = type; this.count = count; } public int getCount() { return count; } public Type getType() { return type; } public EventState getState() { return state; } public static synchronized BiFunction<EventState, Type, TickEvent> createNextProvider() { final int count = overallTickCount++; return (state, type) -> new TickEvent(state, type, count); } public enum Type { /** * When guarantees can be made about * the game state and in-game variables. */ IN, /** * No guarantees can be made about the game state. * This probably means we are at the main menu. */ OUT, } } ================================================ FILE: src/api/java/baritone/api/event/events/WorldEvent.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events; import baritone.api.event.events.type.EventState; import net.minecraft.client.multiplayer.ClientLevel; /** * @author Brady * @since 8/4/2018 */ public final class WorldEvent { /** * The new world that is being loaded. {@code null} if being unloaded. */ private final ClientLevel world; /** * The state of the event */ private final EventState state; public WorldEvent(ClientLevel world, EventState state) { this.world = world; this.state = state; } /** * @return The new world that is being loaded. {@code null} if being unloaded. */ public final ClientLevel getWorld() { return this.world; } /** * @return The state of the event */ public final EventState getState() { return this.state; } } ================================================ FILE: src/api/java/baritone/api/event/events/type/Cancellable.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events.type; /** * @author Brady * @since 8/1/2018 */ public class Cancellable implements ICancellable { /** * Whether or not this event has been cancelled */ private boolean cancelled; @Override public final void cancel() { this.cancelled = true; } @Override public final boolean isCancelled() { return this.cancelled; } } ================================================ FILE: src/api/java/baritone/api/event/events/type/EventState.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events.type; /** * @author Brady * @since 8/2/2018 */ public enum EventState { /** * Before the dispatching of what the event is targetting */ PRE, /** * After the dispatching of what the event is targetting */ POST } ================================================ FILE: src/api/java/baritone/api/event/events/type/ICancellable.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events.type; /** * @author Brady * @since 10/11/2018 */ public interface ICancellable { /** * Cancels this event */ void cancel(); /** * @return Whether or not this event has been cancelled */ boolean isCancelled(); } ================================================ FILE: src/api/java/baritone/api/event/events/type/Overrideable.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.events.type; /** * @author LoganDark */ public class Overrideable<T> { private T value; private boolean modified; public Overrideable(T current) { value = current; } public T get() { return value; } public void set(T newValue) { value = newValue; modified = true; } public boolean wasModified() { return modified; } @Override public String toString() { return String.format( "Overrideable{modified=%b,value=%s}", modified, value.toString() ); } } ================================================ FILE: src/api/java/baritone/api/event/listener/AbstractGameEventListener.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.listener; import baritone.api.event.events.*; /** * An implementation of {@link IGameEventListener} that has all methods * overridden with empty bodies, allowing inheritors of this class to choose * which events they would like to listen in on. * * @author Brady * @see IGameEventListener * @since 8/1/2018 */ public interface AbstractGameEventListener extends IGameEventListener { @Override default void onTick(TickEvent event) {} @Override default void onPostTick(TickEvent event) {} @Override default void onPlayerUpdate(PlayerUpdateEvent event) {} @Override default void onSendChatMessage(ChatEvent event) {} @Override default void onPreTabComplete(TabCompleteEvent event) {} @Override default void onChunkEvent(ChunkEvent event) {} @Override default void onBlockChange(BlockChangeEvent event) {} @Override default void onRenderPass(RenderEvent event) {} @Override default void onWorldEvent(WorldEvent event) {} @Override default void onSendPacket(PacketEvent event) {} @Override default void onReceivePacket(PacketEvent event) {} @Override default void onPlayerRotationMove(RotationMoveEvent event) {} @Override default void onPlayerSprintState(SprintStateEvent event) {} @Override default void onBlockInteract(BlockInteractEvent event) {} @Override default void onPlayerDeath() {} @Override default void onPathEvent(PathEvent event) {} } ================================================ FILE: src/api/java/baritone/api/event/listener/IEventBus.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.listener; /** * A type of {@link IGameEventListener} that can have additional listeners * registered so that they receive the events that are dispatched to this * listener. * * @author Brady * @since 11/14/2018 */ public interface IEventBus extends IGameEventListener { /** * Registers the specified {@link IGameEventListener} to this event bus * * @param listener The listener */ void registerEventListener(IGameEventListener listener); } ================================================ FILE: src/api/java/baritone/api/event/listener/IGameEventListener.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.event.listener; import baritone.api.event.events.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.DeathScreen; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.network.protocol.Packet; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.Vec3; /** * @author Brady * @since 7/31/2018 */ public interface IGameEventListener { /** * Run once per game tick before screen input is handled. * * @param event The event * @see Minecraft#tick() */ void onTick(TickEvent event); /** * Run once per game tick after the tick is completed * * @param event The event * @see Minecraft#runTick() */ void onPostTick(TickEvent event); /** * Run once per game tick from before and after the player rotation is sent to the server. * * @param event The event * @see LocalPlayer#tick() */ void onPlayerUpdate(PlayerUpdateEvent event); /** * Runs whenever the client player sends a message to the server. * * @param event The event * @see LocalPlayer#chat(String) */ void onSendChatMessage(ChatEvent event); /** * Runs whenever the client player tries to tab complete in chat. * * @param event The event */ void onPreTabComplete(TabCompleteEvent event); /** * Runs before and after whenever a chunk is either loaded, unloaded, or populated. * * @param event The event */ void onChunkEvent(ChunkEvent event); /** * Runs after a single or multi block change packet is received and processed. * * @param event The event */ void onBlockChange(BlockChangeEvent event); /** * Runs once per world render pass. * * @param event The event */ void onRenderPass(RenderEvent event); /** * Runs before and after whenever a new world is loaded * * @param event The event * @see Minecraft#setLevel(ClientLevel) */ void onWorldEvent(WorldEvent event); /** * Runs before a outbound packet is sent * * @param event The event * @see Packet */ void onSendPacket(PacketEvent event); /** * Runs before an inbound packet is processed * * @param event The event * @see Packet */ void onReceivePacket(PacketEvent event); /** * Run once per game tick from before and after the player's moveRelative method is called * and before and after the player jumps. * * @param event The event * @see Entity#moveRelative(float, Vec3) */ void onPlayerRotationMove(RotationMoveEvent event); /** * Called whenever the sprint keybind state is checked in {@link LocalPlayer#aiStep} * * @param event The event * @see LocalPlayer#aiStep() */ void onPlayerSprintState(SprintStateEvent event); /** * Called when the local player interacts with a block, whether it is breaking or opening/placing. * * @param event The event */ void onBlockInteract(BlockInteractEvent event); /** * Called when the local player dies, as indicated by the creation of the {@link DeathScreen} screen. * * @see DeathScreen */ void onPlayerDeath(); /** * When the pathfinder's state changes * * @param event The event */ void onPathEvent(PathEvent event); } ================================================ FILE: src/api/java/baritone/api/pathing/calc/IPath.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.calc; import baritone.api.Settings; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.IMovement; import baritone.api.utils.BetterBlockPos; import java.util.HashSet; import java.util.List; /** * @author leijurv, Brady */ public interface IPath { /** * Ordered list of movements to carry out. * movements.get(i).getSrc() should equal positions.get(i) * movements.get(i).getDest() should equal positions.get(i+1) * movements.size() should equal positions.size()-1 * * @return All of the movements to carry out */ List<IMovement> movements(); /** * All positions along the way. * Should begin with the same as getSrc and end with the same as getDest * * @return All of the positions along this path */ List<BetterBlockPos> positions(); /** * This path is actually going to be executed in the world. Do whatever additional processing is required. * (as opposed to Path objects that are just constructed every frame for rendering) * * @return The result of path post processing */ default IPath postProcess() { throw new UnsupportedOperationException(); } /** * Returns the number of positions in this path. Equivalent to {@code positions().size()}. * * @return Number of positions in this path */ default int length() { return positions().size(); } /** * @return The goal that this path was calculated towards */ Goal getGoal(); /** * Returns the number of nodes that were considered during calculation before * this path was found. * * @return The number of nodes that were considered before finding this path */ int getNumNodesConsidered(); /** * Returns the start position of this path. This is the first element in the * {@link List} that is returned by {@link IPath#positions()}. * * @return The start position of this path */ default BetterBlockPos getSrc() { return positions().get(0); } /** * Returns the end position of this path. This is the last element in the * {@link List} that is returned by {@link IPath#positions()}. * * @return The end position of this path. */ default BetterBlockPos getDest() { List<BetterBlockPos> pos = positions(); return pos.get(pos.size() - 1); } /** * Returns the estimated number of ticks to complete the path from the given node index. * * @param pathPosition The index of the node we're calculating from * @return The estimated number of ticks remaining frm the given position */ default double ticksRemainingFrom(int pathPosition) { double sum = 0; //this is fast because we aren't requesting recalculation, it's just cached List<IMovement> movements = movements(); for (int i = pathPosition; i < movements.size(); i++) { sum += movements.get(i).getCost(); } return sum; } /** * Cuts off this path at the loaded chunk border, and returns the resulting path. Default * implementation just returns this path, without the intended functionality. * <p> * The argument is supposed to be a BlockStateInterface LOL LOL LOL LOL LOL * * @param bsi The block state lookup, highly cursed * @return The result of this cut-off operation */ default IPath cutoffAtLoadedChunks(Object bsi) { throw new UnsupportedOperationException(); } /** * Cuts off this path using the min length and cutoff factor settings, and returns the resulting path. * Default implementation just returns this path, without the intended functionality. * * @param destination The end goal of this path * @return The result of this cut-off operation * @see Settings#pathCutoffMinimumLength * @see Settings#pathCutoffFactor */ default IPath staticCutoff(Goal destination) { throw new UnsupportedOperationException(); } /** * Performs a series of checks to ensure that the assembly of the path went as expected. */ default void sanityCheck() { List<BetterBlockPos> path = positions(); List<IMovement> movements = movements(); if (!getSrc().equals(path.get(0))) { throw new IllegalStateException("Start node does not equal first path element"); } if (!getDest().equals(path.get(path.size() - 1))) { throw new IllegalStateException("End node does not equal last path element"); } if (path.size() != movements.size() + 1) { throw new IllegalStateException("Size of path array is unexpected"); } HashSet<BetterBlockPos> seenSoFar = new HashSet<>(); for (int i = 0; i < path.size() - 1; i++) { BetterBlockPos src = path.get(i); BetterBlockPos dest = path.get(i + 1); IMovement movement = movements.get(i); if (!src.equals(movement.getSrc())) { throw new IllegalStateException("Path source is not equal to the movement source"); } if (!dest.equals(movement.getDest())) { throw new IllegalStateException("Path destination is not equal to the movement destination"); } if (seenSoFar.contains(src)) { throw new IllegalStateException("Path doubles back on itself, making a loop"); } seenSoFar.add(src); } } } ================================================ FILE: src/api/java/baritone/api/pathing/calc/IPathFinder.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.calc; import baritone.api.pathing.goals.Goal; import baritone.api.utils.PathCalculationResult; import java.util.Optional; /** * Generic path finder interface * * @author leijurv */ public interface IPathFinder { Goal getGoal(); /** * Calculate the path in full. Will take several seconds. * * @param primaryTimeout If a path is found, the path finder will stop after this amount of time * @param failureTimeout If a path isn't found, the path finder will continue for this amount of time * @return The final path */ PathCalculationResult calculate(long primaryTimeout, long failureTimeout); /** * Intended to be called concurrently with calculatePath from a different thread to tell if it's finished yet * * @return Whether or not this finder is finished */ boolean isFinished(); /** * Called for path rendering. Returns a path to the most recent node popped from the open set and considered. * * @return The temporary path */ Optional<IPath> pathToMostRecentNodeConsidered(); /** * The best path so far, according to the most forgiving coefficient heuristic (the reason being that that path is * most likely to represent the true shape of the path to the goal, assuming it's within a possible cost heuristic. * That's almost always a safe assumption, but in the case of a nearly impossible path, it still works by providing * a theoretically plausible but practically unlikely path) * * @return The temporary path */ Optional<IPath> bestPathSoFar(); } ================================================ FILE: src/api/java/baritone/api/pathing/calc/IPathingControlManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.calc; import baritone.api.process.IBaritoneProcess; import baritone.api.process.PathingCommand; import java.util.Optional; /** * @author leijurv */ public interface IPathingControlManager { /** * Registers a process with this pathing control manager. See {@link IBaritoneProcess} for more details. * * @param process The process * @see IBaritoneProcess */ void registerProcess(IBaritoneProcess process); /** * @return The most recent {@link IBaritoneProcess} that had control */ Optional<IBaritoneProcess> mostRecentInControl(); /** * @return The most recent pathing command executed */ Optional<PathingCommand> mostRecentCommand(); } ================================================ FILE: src/api/java/baritone/api/pathing/goals/Goal.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import net.minecraft.core.BlockPos; /** * An abstract Goal for pathing, can be anything from a specific block to just a Y coordinate. * * @author leijurv */ public interface Goal { /** * Returns whether or not the specified position * meets the requirement for this goal based. * * @param x The goal X position * @param y The goal Y position * @param z The goal Z position * @return Whether or not it satisfies this goal */ boolean isInGoal(int x, int y, int z); /** * Estimate the number of ticks it will take to get to the goal * * @param x The goal X position * @param y The goal Y position * @param z The goal Z position * @return The estimate number of ticks to satisfy the goal */ double heuristic(int x, int y, int z); default boolean isInGoal(BlockPos pos) { return isInGoal(pos.getX(), pos.getY(), pos.getZ()); } default double heuristic(BlockPos pos) { return heuristic(pos.getX(), pos.getY(), pos.getZ()); } /** * Returns the heuristic at the goal. * i.e. {@code heuristic() == heuristic(x,y,z)} * when {@code isInGoal(x,y,z) == true} * This is needed by {@code PathingBehavior#estimatedTicksToGoal} because * some Goals actually do not have a heuristic of 0 when that condition is met * * @return The estimate number of ticks to satisfy the goal when the goal * is already satisfied */ default double heuristic() { return 0; } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalAxis.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.BaritoneAPI; public class GoalAxis implements Goal { private static final double SQRT_2_OVER_2 = Math.sqrt(2) / 2; @Override public boolean isInGoal(int x, int y, int z) { return y == BaritoneAPI.getSettings().axisHeight.value && (x == 0 || z == 0 || Math.abs(x) == Math.abs(z)); } @Override public double heuristic(int x0, int y, int z0) { int x = Math.abs(x0); int z = Math.abs(z0); int shrt = Math.min(x, z); int lng = Math.max(x, z); int diff = lng - shrt; double flatAxisDistance = Math.min(x, Math.min(z, diff * SQRT_2_OVER_2)); return flatAxisDistance * BaritoneAPI.getSettings().costHeuristic.value + GoalYLevel.calculate(BaritoneAPI.getSettings().axisHeight.value, y); } @Override public boolean equals(Object o) { return o.getClass() == GoalAxis.class; } @Override public int hashCode() { return 201385781; } @Override public String toString() { return "GoalAxis"; } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalBlock.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.core.BlockPos; /** * A specific BlockPos goal * * @author leijurv */ public class GoalBlock implements Goal, IGoalRenderPos { /** * The X block position of this goal */ public final int x; /** * The Y block position of this goal */ public final int y; /** * The Z block position of this goal */ public final int z; public GoalBlock(BlockPos pos) { this(pos.getX(), pos.getY(), pos.getZ()); } public GoalBlock(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } @Override public boolean isInGoal(int x, int y, int z) { return x == this.x && y == this.y && z == this.z; } @Override public double heuristic(int x, int y, int z) { int xDiff = x - this.x; int yDiff = y - this.y; int zDiff = z - this.z; return calculate(xDiff, yDiff, zDiff); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalBlock goal = (GoalBlock) o; return x == goal.x && y == goal.y && z == goal.z; } @Override public int hashCode() { return (int) BetterBlockPos.longHash(x, y, z) * 905165533; } @Override public String toString() { return String.format( "GoalBlock{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } /** * @return The position of this goal as a {@link BlockPos} */ @Override public BlockPos getGoalPos() { return new BlockPos(x, y, z); } public static double calculate(double xDiff, int yDiff, double zDiff) { double heuristic = 0; // if yDiff is 1 that means that currentY-goalY==1 which means that we're 1 block above where we should be // therefore going from 0,yDiff,0 to a GoalYLevel of 0 is accurate heuristic += GoalYLevel.calculate(0, yDiff); //use the pythagorean and manhattan mixture from GoalXZ heuristic += GoalXZ.calculate(xDiff, zDiff); return heuristic; } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalComposite.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import java.util.Arrays; /** * A composite of many goals, any one of which satisfies the composite. * For example, a GoalComposite of block goals for every oak log in loaded chunks * would result in it pathing to the easiest oak log to get to * * @author avecowa */ public class GoalComposite implements Goal { /** * An array of goals that any one of must be satisfied */ private final Goal[] goals; public GoalComposite(Goal... goals) { this.goals = goals; } @Override public boolean isInGoal(int x, int y, int z) { for (Goal goal : goals) { if (goal.isInGoal(x, y, z)) { return true; } } return false; } @Override public double heuristic(int x, int y, int z) { double min = Double.MAX_VALUE; for (Goal g : goals) { // TODO technically this isn't admissible...? min = Math.min(min, g.heuristic(x, y, z)); // whichever is closest } return min; } @Override public double heuristic() { double min = Double.MAX_VALUE; for (Goal g : goals) { // just take the highest value that is guaranteed to be inside the goal min = Math.min(min, g.heuristic()); } return min; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalComposite goal = (GoalComposite) o; return Arrays.equals(goals, goal.goals); } @Override public int hashCode() { return Arrays.hashCode(goals); } @Override public String toString() { return "GoalComposite" + Arrays.toString(goals); } public Goal[] goals() { return goals; } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.core.BlockPos; /** * Don't get into the block, but get directly adjacent to it. Useful for chests. * * @author avecowa */ public class GoalGetToBlock implements Goal, IGoalRenderPos { public final int x; public final int y; public final int z; public GoalGetToBlock(BlockPos pos) { this.x = pos.getX(); this.y = pos.getY(); this.z = pos.getZ(); } @Override public BlockPos getGoalPos() { return new BlockPos(x, y, z); } @Override public boolean isInGoal(int x, int y, int z) { int xDiff = x - this.x; int yDiff = y - this.y; int zDiff = z - this.z; return Math.abs(xDiff) + Math.abs(yDiff < 0 ? yDiff + 1 : yDiff) + Math.abs(zDiff) <= 1; } @Override public double heuristic(int x, int y, int z) { int xDiff = x - this.x; int yDiff = y - this.y; int zDiff = z - this.z; return GoalBlock.calculate(xDiff, yDiff < 0 ? yDiff + 1 : yDiff, zDiff); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalGetToBlock goal = (GoalGetToBlock) o; return x == goal.x && y == goal.y && z == goal.z; } @Override public int hashCode() { return (int) BetterBlockPos.longHash(x, y, z) * -49639096; } @Override public String toString() { return String.format( "GoalGetToBlock{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalInverted.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import java.util.Objects; /** * Invert any goal. * <p> * In the old chat control system, #invert just tried to pick a {@link GoalRunAway} that <i>effectively</i> inverted the * current goal. This goal just reverses the heuristic to act as a TRUE invert. Inverting a Y level? Baritone tries to * get away from that Y level. Inverting a GoalBlock? Baritone will try to make distance whether it's in the X, Y or Z * directions. And of course, you can always invert a GoalXZ. * * @author LoganDark */ public class GoalInverted implements Goal { public final Goal origin; public GoalInverted(Goal origin) { this.origin = origin; } @Override public boolean isInGoal(int x, int y, int z) { return false; } @Override public double heuristic(int x, int y, int z) { return -origin.heuristic(x, y, z); } @Override public double heuristic() { return Double.NEGATIVE_INFINITY; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalInverted goal = (GoalInverted) o; return Objects.equals(origin, goal.origin); } @Override public int hashCode() { return origin.hashCode() * 495796690; } @Override public String toString() { return String.format("GoalInverted{%s}", origin.toString()); } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalNear.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import it.unimi.dsi.fastutil.doubles.DoubleIterator; import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet; import net.minecraft.core.BlockPos; public class GoalNear implements Goal, IGoalRenderPos { private final int x; private final int y; private final int z; private final int rangeSq; public GoalNear(BlockPos pos, int range) { this.x = pos.getX(); this.y = pos.getY(); this.z = pos.getZ(); this.rangeSq = range * range; } @Override public boolean isInGoal(int x, int y, int z) { int xDiff = x - this.x; int yDiff = y - this.y; int zDiff = z - this.z; return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff <= rangeSq; } @Override public double heuristic(int x, int y, int z) { int xDiff = x - this.x; int yDiff = y - this.y; int zDiff = z - this.z; return GoalBlock.calculate(xDiff, yDiff, zDiff); } @Override public double heuristic() {// TODO less hacky solution int range = (int) Math.ceil(Math.sqrt(rangeSq)); DoubleOpenHashSet maybeAlwaysInside = new DoubleOpenHashSet(); // see pull request #1978 double minOutside = Double.POSITIVE_INFINITY; for (int dx = -range; dx <= range; dx++) { for (int dy = -range; dy <= range; dy++) { for (int dz = -range; dz <= range; dz++) { double h = heuristic(x + dx, y + dy, z + dz); if (h < minOutside && isInGoal(x + dx, y + dy, z + dz)) { maybeAlwaysInside.add(h); } else { minOutside = Math.min(minOutside, h); } } } } double maxInside = Double.NEGATIVE_INFINITY; DoubleIterator it = maybeAlwaysInside.iterator(); while (it.hasNext()) { double inside = it.nextDouble(); if (inside < minOutside) { maxInside = Math.max(maxInside, inside); } } return maxInside; } @Override public BlockPos getGoalPos() { return new BlockPos(x, y, z); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalNear goal = (GoalNear) o; return x == goal.x && y == goal.y && z == goal.z && rangeSq == goal.rangeSq; } @Override public int hashCode() { return (int) BetterBlockPos.longHash(x, y, z) + rangeSq; } @Override public String toString() { return String.format( "GoalNear{x=%s, y=%s, z=%s, rangeSq=%d}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z), rangeSq ); } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalRunAway.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.utils.SettingsUtil; import it.unimi.dsi.fastutil.doubles.DoubleIterator; import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet; import net.minecraft.core.BlockPos; import java.util.Arrays; import java.util.Objects; /** * Useful for automated combat (retreating specifically) * * @author leijurv */ public class GoalRunAway implements Goal { private final BlockPos[] from; private final int distanceSq; private final Integer maintainY; public GoalRunAway(double distance, BlockPos... from) { this(distance, null, from); } public GoalRunAway(double distance, Integer maintainY, BlockPos... from) { if (from.length == 0) { throw new IllegalArgumentException("Positions to run away from must not be empty"); } this.from = from; this.distanceSq = (int) (distance * distance); this.maintainY = maintainY; } @Override public boolean isInGoal(int x, int y, int z) { if (maintainY != null && maintainY != y) { return false; } for (BlockPos p : from) { int diffX = x - p.getX(); int diffZ = z - p.getZ(); int distSq = diffX * diffX + diffZ * diffZ; if (distSq < distanceSq) { return false; } } return true; } @Override public double heuristic(int x, int y, int z) {// mostly copied from GoalBlock double min = Double.MAX_VALUE; for (BlockPos p : from) { double h = GoalXZ.calculate(p.getX() - x, p.getZ() - z); if (h < min) { min = h; } } min = -min; if (maintainY != null) { min = min * 0.6 + GoalYLevel.calculate(maintainY, y) * 1.5; } return min; } @Override public double heuristic() {// TODO less hacky solution int distance = (int) Math.ceil(Math.sqrt(distanceSq)); int minX = Integer.MAX_VALUE; int minY = Integer.MAX_VALUE; int minZ = Integer.MAX_VALUE; int maxX = Integer.MIN_VALUE; int maxY = Integer.MIN_VALUE; int maxZ = Integer.MIN_VALUE; for (BlockPos p : from) { minX = Math.min(minX, p.getX() - distance); minY = Math.min(minY, p.getY() - distance); minZ = Math.min(minZ, p.getZ() - distance); maxX = Math.max(minX, p.getX() + distance); maxY = Math.max(minY, p.getY() + distance); maxZ = Math.max(minZ, p.getZ() + distance); } DoubleOpenHashSet maybeAlwaysInside = new DoubleOpenHashSet(); // see pull request #1978 double minOutside = Double.POSITIVE_INFINITY; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { double h = heuristic(x, y, z); if (h < minOutside && isInGoal(x, y, z)) { maybeAlwaysInside.add(h); } else { minOutside = Math.min(minOutside, h); } } } } double maxInside = Double.NEGATIVE_INFINITY; DoubleIterator it = maybeAlwaysInside.iterator(); while (it.hasNext()) { double inside = it.nextDouble(); if (inside < minOutside) { maxInside = Math.max(maxInside, inside); } } return maxInside; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalRunAway goal = (GoalRunAway) o; return distanceSq == goal.distanceSq && Arrays.equals(from, goal.from) && Objects.equals(maintainY, goal.maintainY); } @Override public int hashCode() { int hash = Arrays.hashCode(from); hash = hash * 1196803141 + distanceSq; hash = hash * -2053788840 + maintainY; return hash; } @Override public String toString() { if (maintainY != null) { return String.format( "GoalRunAwayFromMaintainY y=%s, %s", SettingsUtil.maybeCensor(maintainY), Arrays.asList(from) ); } else { return "GoalRunAwayFrom" + Arrays.asList(from); } } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; /** * Dig a tunnel in a certain direction, but if you have to deviate from the path, go back to where you started */ public class GoalStrictDirection implements Goal { public final int x; public final int y; public final int z; public final int dx; public final int dz; public GoalStrictDirection(BlockPos origin, Direction direction) { x = origin.getX(); y = origin.getY(); z = origin.getZ(); dx = direction.getStepX(); dz = direction.getStepZ(); if (dx == 0 && dz == 0) { throw new IllegalArgumentException(direction + ""); } } @Override public boolean isInGoal(int x, int y, int z) { return false; } @Override public double heuristic(int x, int y, int z) { int distanceFromStartInDesiredDirection = (x - this.x) * dx + (z - this.z) * dz; int distanceFromStartInIncorrectDirection = Math.abs((x - this.x) * dz) + Math.abs((z - this.z) * dx); int verticalDistanceFromStart = Math.abs(y - this.y); // we want heuristic to decrease as desiredDirection increases double heuristic = -distanceFromStartInDesiredDirection * 100; heuristic += distanceFromStartInIncorrectDirection * 1000; heuristic += verticalDistanceFromStart * 1000; return heuristic; } @Override public double heuristic() { return Double.NEGATIVE_INFINITY; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalStrictDirection goal = (GoalStrictDirection) o; return x == goal.x && y == goal.y && z == goal.z && dx == goal.dx && dz == goal.dz; } @Override public int hashCode() { int hash = (int) BetterBlockPos.longHash(x, y, z); hash = hash * 630627507 + dx; hash = hash * -283028380 + dz; return hash; } @Override public String toString() { return String.format( "GoalStrictDirection{x=%s, y=%s, z=%s, dx=%s, dz=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z), SettingsUtil.maybeCensor(dx), SettingsUtil.maybeCensor(dz) ); } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.core.BlockPos; /** * Useful if the goal is just to mine a block. This goal will be satisfied if the specified * {@link BlockPos} is at to or above the specified position for this goal. * * @author leijurv */ public class GoalTwoBlocks implements Goal, IGoalRenderPos { /** * The X block position of this goal */ protected final int x; /** * The Y block position of this goal */ protected final int y; /** * The Z block position of this goal */ protected final int z; public GoalTwoBlocks(BlockPos pos) { this(pos.getX(), pos.getY(), pos.getZ()); } public GoalTwoBlocks(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } @Override public boolean isInGoal(int x, int y, int z) { return x == this.x && (y == this.y || y == this.y - 1) && z == this.z; } @Override public double heuristic(int x, int y, int z) { int xDiff = x - this.x; int yDiff = y - this.y; int zDiff = z - this.z; return GoalBlock.calculate(xDiff, yDiff < 0 ? yDiff + 1 : yDiff, zDiff); } @Override public BlockPos getGoalPos() { return new BlockPos(x, y, z); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalTwoBlocks goal = (GoalTwoBlocks) o; return x == goal.x && y == goal.y && z == goal.z; } @Override public int hashCode() { return (int) BetterBlockPos.longHash(x, y, z) * 516508351; } @Override public String toString() { return String.format( "GoalTwoBlocks{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalXZ.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.BaritoneAPI; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; /** * Useful for long-range goals that don't have a specific Y level. * * @author leijurv */ public class GoalXZ implements Goal { private static final double SQRT_2 = Math.sqrt(2); /** * The X block position of this goal */ private final int x; /** * The Z block position of this goal */ private final int z; public GoalXZ(int x, int z) { this.x = x; this.z = z; } public GoalXZ(BetterBlockPos pos) { this.x = pos.x; this.z = pos.z; } @Override public boolean isInGoal(int x, int y, int z) { return x == this.x && z == this.z; } @Override public double heuristic(int x, int y, int z) {//mostly copied from GoalBlock int xDiff = x - this.x; int zDiff = z - this.z; return calculate(xDiff, zDiff); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalXZ goal = (GoalXZ) o; return x == goal.x && z == goal.z; } @Override public int hashCode() { int hash = 1791873246; hash = hash * 222601791 + x; hash = hash * -1331679453 + z; return hash; } @Override public String toString() { return String.format( "GoalXZ{x=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(z) ); } public static double calculate(double xDiff, double zDiff) { //This is a combination of pythagorean and manhattan distance //It takes into account the fact that pathing can either walk diagonally or forwards //It's not possible to walk forward 1 and right 2 in sqrt(5) time //It's really 1+sqrt(2) because it'll walk forward 1 then diagonally 1 double x = Math.abs(xDiff); double z = Math.abs(zDiff); double straight; double diagonal; if (x < z) { straight = z - x; diagonal = x; } else { straight = x - z; diagonal = z; } diagonal *= SQRT_2; return (diagonal + straight) * BaritoneAPI.getSettings().costHeuristic.value; // big TODO tune } public static GoalXZ fromDirection(Vec3 origin, float yaw, double distance) { float theta = (float) Math.toRadians(yaw); double x = origin.x - Mth.sin(theta) * distance; double z = origin.z + Mth.cos(theta) * distance; return new GoalXZ(Mth.floor(x), Mth.floor(z)); } public int getX() { return x; } public int getZ() { return z; } } ================================================ FILE: src/api/java/baritone/api/pathing/goals/GoalYLevel.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.goals; import baritone.api.pathing.movement.ActionCosts; import baritone.api.utils.SettingsUtil; /** * Useful for mining (getting to diamond / iron level) * * @author leijurv */ public class GoalYLevel implements Goal, ActionCosts { /** * The target Y level */ public final int level; public GoalYLevel(int level) { this.level = level; } @Override public boolean isInGoal(int x, int y, int z) { return y == level; } @Override public double heuristic(int x, int y, int z) { return calculate(level, y); } public static double calculate(int goalY, int currentY) { if (currentY > goalY) { // need to descend return FALL_N_BLOCKS_COST[2] / 2 * (currentY - goalY); } if (currentY < goalY) { // need to ascend return (goalY - currentY) * JUMP_ONE_BLOCK_COST; } return 0; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GoalYLevel goal = (GoalYLevel) o; return level == goal.level; } @Override public int hashCode() { return level * 1271009915; } @Override public String toString() { return String.format( "GoalYLevel{y=%s}", SettingsUtil.maybeCensor(level) ); } } ================================================ FILE: src/api/java/baritone/api/pathing/movement/ActionCosts.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.movement; public interface ActionCosts { /** * These costs are measured roughly in ticks btw */ double WALK_ONE_BLOCK_COST = 20 / 4.317; // 4.633 double WALK_ONE_IN_WATER_COST = 20 / 2.2; // 9.091 double WALK_ONE_OVER_SOUL_SAND_COST = WALK_ONE_BLOCK_COST * 2; // 0.4 in BlockSoulSand but effectively about half double LADDER_UP_ONE_COST = 20 / 2.35; // 8.511 double LADDER_DOWN_ONE_COST = 20 / 3.0; // 6.667 double SNEAK_ONE_BLOCK_COST = 20 / 1.3; // 15.385 double SPRINT_ONE_BLOCK_COST = 20 / 5.612; // 3.564 double SPRINT_MULTIPLIER = SPRINT_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST; // 0.769 /** * To walk off an edge you need to walk 0.5 to the edge then 0.3 to start falling off */ double WALK_OFF_BLOCK_COST = WALK_ONE_BLOCK_COST * 0.8; // 3.706 /** * To walk the rest of the way to be centered on the new block */ double CENTER_AFTER_FALL_COST = WALK_ONE_BLOCK_COST - WALK_OFF_BLOCK_COST; // 0.927 /** * don't make this Double.MAX_VALUE because it's added to other things, maybe other COST_INFs, * and that would make it overflow to negative */ double COST_INF = 1000000; double[] FALL_N_BLOCKS_COST = generateFallNBlocksCost(); double FALL_1_25_BLOCKS_COST = distanceToTicks(1.25); double FALL_0_25_BLOCKS_COST = distanceToTicks(0.25); /** * When you hit space, you get enough upward velocity to go 1.25 blocks * Then, you fall the remaining 0.25 to get on the surface, on block higher. * Since parabolas are symmetric, the amount of time it takes to ascend up from 1 to 1.25 * will be the same amount of time that it takes to fall back down from 1.25 to 1. * And the same applies to the overall shape, if it takes X ticks to fall back down 1.25 blocks, * it will take X ticks to reach the peak of your 1.25 block leap. * Therefore, the part of your jump from y=0 to y=1.25 takes distanceToTicks(1.25) ticks, * and the sub-part from y=1 to y=1.25 takes distanceToTicks(0.25) ticks. * Therefore, the other sub-part, from y=0 to y-1, takes distanceToTicks(1.25)-distanceToTicks(0.25) ticks. * That's why JUMP_ONE_BLOCK_COST = FALL_1_25_BLOCKS_COST - FALL_0_25_BLOCKS_COST */ double JUMP_ONE_BLOCK_COST = FALL_1_25_BLOCKS_COST - FALL_0_25_BLOCKS_COST; static double[] generateFallNBlocksCost() { double[] costs = new double[4097]; for (int i = 0; i < 4097; i++) { costs[i] = distanceToTicks(i); } return costs; } static double velocity(int ticks) { return (Math.pow(0.98, ticks) - 1) * -3.92; } static double oldFormula(double ticks) { return -3.92 * (99 - 49.5 * (Math.pow(0.98, ticks) + 1) - ticks); } static double distanceToTicks(double distance) { if (distance == 0) { return 0; // Avoid 0/0 NaN } double tmpDistance = distance; int tickCount = 0; while (true) { double fallDistance = velocity(tickCount); if (tmpDistance <= fallDistance) { return tickCount + tmpDistance / fallDistance; } tmpDistance -= fallDistance; tickCount++; } } } ================================================ FILE: src/api/java/baritone/api/pathing/movement/IMovement.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.movement; import baritone.api.utils.BetterBlockPos; import net.minecraft.core.BlockPos; /** * @author Brady * @since 10/8/2018 */ public interface IMovement { double getCost(); MovementStatus update(); /** * Resets the current state status to {@link MovementStatus#PREPPING} */ void reset(); /** * Resets the cache for special break, place, and walk into blocks */ void resetBlockCache(); /** * @return Whether or not it is safe to cancel the current movement state */ boolean safeToCancel(); boolean calculatedWhileLoaded(); BetterBlockPos getSrc(); BetterBlockPos getDest(); BlockPos getDirection(); } ================================================ FILE: src/api/java/baritone/api/pathing/movement/MovementStatus.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.movement; /** * @author Brady * @since 10/8/2018 */ public enum MovementStatus { /** * We are preparing the movement to be executed. This is when any blocks obstructing the destination are broken. */ PREPPING(false), /** * We are waiting for the movement to begin, after {@link MovementStatus#PREPPING}. */ WAITING(false), /** * The movement is currently in progress, after {@link MovementStatus#WAITING} */ RUNNING(false), /** * The movement has been completed and we are at our destination */ SUCCESS(true), /** * There was a change in state between calculation and actual * movement execution, and the movement has now become impossible. */ UNREACHABLE(true), /** * Unused */ FAILED(true), /** * "Unused" */ CANCELED(true); /** * Whether or not this status indicates a complete movement. */ private final boolean complete; MovementStatus(boolean complete) { this.complete = complete; } public final boolean isComplete() { return this.complete; } } ================================================ FILE: src/api/java/baritone/api/pathing/path/IPathExecutor.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.pathing.path; import baritone.api.pathing.calc.IPath; /** * @author Brady * @since 10/8/2018 */ public interface IPathExecutor { IPath getPath(); int getPosition(); } ================================================ FILE: src/api/java/baritone/api/process/IBaritoneProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.behavior.IPathingBehavior; import baritone.api.event.events.PathEvent; /** * A process that can control the PathingBehavior. * <p> * Differences between a baritone process and a behavior: * <ul> * <li>Only one baritone process can be active at a time</li> * <li>PathingBehavior can only be controlled by a process</li> * </ul> * <p> * That's it actually * * @author leijurv */ public interface IBaritoneProcess { /** * Default priority. Most normal processes should have this value. * <p> * Some examples of processes that should have different values might include some kind of automated mob avoidance * that would be temporary and would forcefully take control. Same for something that pauses pathing for auto eat, etc. * <p> * The value is -1 beacuse that's what Impact 4.5's beta auto walk returns and I want to tie with it. */ double DEFAULT_PRIORITY = -1; /** * Would this process like to be in control? * * @return Whether or not this process would like to be in contorl. */ boolean isActive(); /** * Called when this process is in control of pathing; Returns what Baritone should do. * * @param calcFailed {@code true} if this specific process was in control last tick, * and there was a {@link PathEvent#CALC_FAILED} event last tick * @param isSafeToCancel {@code true} if a {@link PathingCommandType#REQUEST_PAUSE} would happen this tick, and * {@link IPathingBehavior} wouldn't actually tick. {@code false} if the PathExecutor reported * pausing would be unsafe at the end of the last tick. Effectively "could request cancel or * pause and have it happen right away" * @return What the {@link IPathingBehavior} should do */ PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel); /** * Returns whether or not this process should be treated as "temporary". * <p> * If a process is temporary, it doesn't call {@link #onLostControl} on the processes that aren't execute because of it. * <p> * For example, {@code CombatPauserProcess} and {@code PauseForAutoEatProcess} should return {@code true} always, * and should return {@link #isActive} {@code true} only if there's something in range this tick, or if the player would like * to start eating this tick. {@code PauseForAutoEatProcess} should only actually right click once onTick is called with * {@code isSafeToCancel} true though. * * @return Whether or not if this control is temporary */ boolean isTemporary(); /** * Called if {@link #isActive} returned {@code true}, but another non-temporary * process has control. Effectively the same as cancel. You want control but you * don't get it. */ void onLostControl(); /** * Used to determine which Process gains control if multiple are reporting {@link #isActive()}. The one * that returns the highest value will be given control. * * @return A double representing the priority */ default double priority() { return DEFAULT_PRIORITY; } /** * Returns a user-friendly name for this process. Suitable for a HUD. * * @return A display name that's suitable for a HUD */ default String displayName() { if (!isActive()) { // i love it when impcat's scuffed HUD calls displayName for inactive processes for 1 tick too long // causing NPEs when the displayname relies on fields that become null when inactive return "INACTIVE"; } return displayName0(); } String displayName0(); } ================================================ FILE: src/api/java/baritone/api/process/IBuilderProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.schematic.ISchematic; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.world.level.block.state.BlockState; import java.io.File; import java.util.List; import java.util.Optional; /** * @author Brady * @since 1/15/2019 */ public interface IBuilderProcess extends IBaritoneProcess { /** * Requests a build for the specified schematic, labeled as specified, with the specified origin. * * @param name A user-friendly name for the schematic * @param schematic The object representation of the schematic * @param origin The origin position of the schematic being built */ void build(String name, ISchematic schematic, Vec3i origin); /** * Requests a build for the specified schematic, labeled as specified, with the specified origin. * * @param name A user-friendly name for the schematic * @param schematic The file path of the schematic * @param origin The origin position of the schematic being built * @return Whether or not the schematic was able to load from file */ boolean build(String name, File schematic, Vec3i origin); @Deprecated default boolean build(String schematicFile, BlockPos origin) { File file = new File(new File(Minecraft.getInstance().gameDirectory, "schematics"), schematicFile); return build(schematicFile, file, origin); } void buildOpenSchematic(); void buildOpenLitematic(int i); void pause(); boolean isPaused(); void resume(); void clearArea(BlockPos corner1, BlockPos corner2); /** * @return A list of block states that are estimated to be placeable by this builder process. You can use this in * schematics, for example, to pick a state that the builder process will be happy with, because any variation will * cause it to give up. This is updated every tick, but only while the builder process is active. */ List<BlockState> getApproxPlaceable(); /** * Returns the lower bound of the current mining layer if mineInLayers is true. * If mineInLayers is false, this will return an empty optional. * @return The lower bound of the current mining layer */ Optional<Integer> getMinLayer(); /** * Returns the upper bound of the current mining layer if mineInLayers is true. * If mineInLayers is false, this will return an empty optional. * @return The upper bound of the current mining layer */ Optional<Integer> getMaxLayer(); } ================================================ FILE: src/api/java/baritone/api/process/ICustomGoalProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.pathing.goals.Goal; public interface ICustomGoalProcess extends IBaritoneProcess { /** * Sets the pathing goal * * @param goal The new goal */ void setGoal(Goal goal); /** * Starts path calculation and execution. */ void path(); /** * @return The current goal */ Goal getGoal(); /** * @return The most recent set goal, which doesn't invalidate upon {@link #onLostControl()} */ Goal mostRecentGoal(); /** * Sets the goal and begins the path execution. * * @param goal The new goal */ default void setGoalAndPath(Goal goal) { this.setGoal(goal); this.path(); } } ================================================ FILE: src/api/java/baritone/api/process/IElytraProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.pathing.goals.Goal; import baritone.api.utils.BetterBlockPos; import net.minecraft.core.BlockPos; import java.util.List; public interface IElytraProcess extends IBaritoneProcess { void repackChunks(); /** * @return Where it is currently flying to, null if not active */ BlockPos currentDestination(); /** * @return Current active path, empty if not active or no path has been calculated yet */ List<BetterBlockPos> getPath(); void pathTo(BlockPos destination); void pathTo(Goal destination); /** * Resets the state of the process but will maintain the same destination and will try to keep flying */ void resetState(); /** * @return {@code true} if the native library loaded and elytra is actually usable */ boolean isLoaded(); /* * FOR INTERNAL USE ONLY. MAY BE REMOVED AT ANY TIME. */ boolean isSafeToCancel(); } ================================================ FILE: src/api/java/baritone/api/process/IExploreProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import java.nio.file.Path; public interface IExploreProcess extends IBaritoneProcess { void explore(int centerX, int centerZ); void applyJsonFilter(Path path, boolean invert) throws Exception; } ================================================ FILE: src/api/java/baritone/api/process/IFarmProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import net.minecraft.core.BlockPos; public interface IFarmProcess extends IBaritoneProcess { /** * Begin to search for crops to farm with in specified aria * from specified location. * * @param range The distance from center to farm from * @param pos The center position to base the range from */ void farm(int range, BlockPos pos); /** * Begin to search for nearby crops to farm. */ default void farm() {farm(0, null);} /** * Begin to search for crops to farm with in specified aria * from the position the command was executed. * * @param range The distance to search for crops to farm */ default void farm(int range) {farm(range, null);} } ================================================ FILE: src/api/java/baritone/api/process/IFollowProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemStack; import java.util.List; import java.util.function.Predicate; /** * @author Brady * @since 9/23/2018 */ public interface IFollowProcess extends IBaritoneProcess { /** * Set the follow target to any entities matching this predicate * * @param filter the predicate */ void follow(Predicate<Entity> filter); /** * Try to pick up any items matching this predicate * * @param filter the predicate */ void pickup(Predicate<ItemStack> filter); /** * @return The entities that are currently being followed. null if not currently following, empty if nothing matches the predicate */ List<Entity> following(); Predicate<Entity> currentFilter(); /** * Cancels the follow behavior, this will clear the current follow target. */ default void cancel() { onLostControl(); } } ================================================ FILE: src/api/java/baritone/api/process/IGetToBlockProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.utils.BlockOptionalMeta; import net.minecraft.world.level.block.Block; /** * but it rescans the world every once in a while so it doesn't get fooled by its cache */ public interface IGetToBlockProcess extends IBaritoneProcess { void getToBlock(BlockOptionalMeta block); default void getToBlock(Block block) { getToBlock(new BlockOptionalMeta(block)); } boolean blacklistClosest(); } ================================================ FILE: src/api/java/baritone/api/process/IMineProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.utils.BlockOptionalMeta; import baritone.api.utils.BlockOptionalMetaLookup; import java.util.stream.Stream; import net.minecraft.world.level.block.Block; /** * @author Brady * @since 9/23/2018 */ public interface IMineProcess extends IBaritoneProcess { /** * Begin to search for and mine the specified blocks until * the number of specified items to get from the blocks that * are mined. * * @param quantity The total number of items to get * @param blocks The blocks to mine */ void mineByName(int quantity, String... blocks); /** * Begin to search for and mine the specified blocks until * the number of specified items to get from the blocks that * are mined. This is based on the first target block to mine. * * @param quantity The number of items to get from blocks mined * @param filter The blocks to mine */ void mine(int quantity, BlockOptionalMetaLookup filter); /** * Begin to search for and mine the specified blocks. * * @param filter The blocks to mine */ default void mine(BlockOptionalMetaLookup filter) { mine(0, filter); } /** * Begin to search for and mine the specified blocks. * * @param blocks The blocks to mine */ default void mineByName(String... blocks) { mineByName(0, blocks); } /** * Begin to search for and mine the specified blocks. * * @param boms The blocks to mine */ default void mine(int quantity, BlockOptionalMeta... boms) { mine(quantity, new BlockOptionalMetaLookup(boms)); } /** * Begin to search for and mine the specified blocks. * * @param boms The blocks to mine */ default void mine(BlockOptionalMeta... boms) { mine(0, boms); } /** * Begin to search for and mine the specified blocks. * * @param quantity The total number of items to get * @param blocks The blocks to mine */ default void mine(int quantity, Block... blocks) { mine(quantity, new BlockOptionalMetaLookup( Stream.of(blocks) .map(BlockOptionalMeta::new) .toArray(BlockOptionalMeta[]::new) )); } /** * Begin to search for and mine the specified blocks. * * @param blocks The blocks to mine */ default void mine(Block... blocks) { mine(0, blocks); } /** * Cancels the current mining task */ default void cancel() { onLostControl(); } } ================================================ FILE: src/api/java/baritone/api/process/PathingCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.pathing.goals.Goal; import java.util.Objects; /** * @author leijurv */ public class PathingCommand { /** * The target goal, may be {@code null}. */ public final Goal goal; /** * The command type. * * @see PathingCommandType */ public final PathingCommandType commandType; /** * Create a new {@link PathingCommand}. * * @param goal The target goal, may be {@code null}. * @param commandType The command type, cannot be {@code null}. * @throws NullPointerException if {@code commandType} is {@code null}. * @see Goal * @see PathingCommandType */ public PathingCommand(Goal goal, PathingCommandType commandType) { Objects.requireNonNull(commandType); this.goal = goal; this.commandType = commandType; } @Override public String toString() { return commandType + " " + goal; } } ================================================ FILE: src/api/java/baritone/api/process/PathingCommandType.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.process; import baritone.api.Settings; public enum PathingCommandType { /** * Set the goal and path. * <p> * If you use this alongside a {@code null} goal, it will continue along its current path and current goal. */ SET_GOAL_AND_PATH, /** * Has no effect on the current goal or path, just requests a pause */ REQUEST_PAUSE, /** * Set the goal (regardless of {@code null}), and request a cancel of the current path (when safe) */ CANCEL_AND_SET_GOAL, /** * Set the goal and path. * <p> * If {@link Settings#cancelOnGoalInvalidation} is {@code true}, revalidate the * current goal, and cancel if it's no longer valid, or if the new goal is {@code null}. */ REVALIDATE_GOAL_AND_PATH, /** * Set the goal and path. * <p> * Cancel the current path if the goals are not equal */ FORCE_REVALIDATE_GOAL_AND_PATH, /** * Go and ask the next process what to do */ DEFER, /** * Sets the goal and calculates a path, but pauses instead of immediately starting the path. */ SET_GOAL_AND_PAUSE } ================================================ FILE: src/api/java/baritone/api/schematic/AbstractSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; public abstract class AbstractSchematic implements ISchematic { protected int x; protected int y; protected int z; public AbstractSchematic() { this(0, 0, 0); } public AbstractSchematic(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } @Override public int widthX() { return x; } @Override public int heightY() { return y; } @Override public int lengthZ() { return z; } } ================================================ FILE: src/api/java/baritone/api/schematic/CompositeSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import java.util.ArrayList; import java.util.List; import net.minecraft.world.level.block.state.BlockState; public class CompositeSchematic extends AbstractSchematic { private final List<CompositeSchematicEntry> schematics; private CompositeSchematicEntry[] schematicArr; private void recalcArr() { schematicArr = schematics.toArray(new CompositeSchematicEntry[0]); for (CompositeSchematicEntry entry : schematicArr) { this.x = Math.max(x, entry.x + entry.schematic.widthX()); this.y = Math.max(y, entry.y + entry.schematic.heightY()); this.z = Math.max(z, entry.z + entry.schematic.lengthZ()); } } public CompositeSchematic(int x, int y, int z) { super(x, y, z); schematics = new ArrayList<>(); recalcArr(); } public void put(ISchematic extra, int x, int y, int z) { schematics.add(new CompositeSchematicEntry(extra, x, y, z)); recalcArr(); } private CompositeSchematicEntry getSchematic(int x, int y, int z, BlockState currentState) { for (CompositeSchematicEntry entry : schematicArr) { if (x >= entry.x && y >= entry.y && z >= entry.z && entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState)) { return entry; } } return null; } @Override public boolean inSchematic(int x, int y, int z, BlockState currentState) { CompositeSchematicEntry entry = getSchematic(x, y, z, currentState); return entry != null && entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState); } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { CompositeSchematicEntry entry = getSchematic(x, y, z, current); if (entry == null) { throw new IllegalStateException("couldn't find schematic for this position"); } return entry.schematic.desiredState(x - entry.x, y - entry.y, z - entry.z, current, approxPlaceable); } @Override public void reset() { for (CompositeSchematicEntry entry : schematicArr) { entry.schematic.reset(); } } } ================================================ FILE: src/api/java/baritone/api/schematic/CompositeSchematicEntry.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; public class CompositeSchematicEntry { public final ISchematic schematic; public final int x; public final int y; public final int z; public CompositeSchematicEntry(ISchematic schematic, int x, int y, int z) { this.schematic = schematic; this.x = x; this.y = y; this.z = z; } } ================================================ FILE: src/api/java/baritone/api/schematic/FillSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import baritone.api.utils.BlockOptionalMeta; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import java.util.List; public class FillSchematic extends AbstractSchematic { private final BlockOptionalMeta bom; public FillSchematic(int x, int y, int z, BlockOptionalMeta bom) { super(x, y, z); this.bom = bom; } public FillSchematic(int x, int y, int z, BlockState state) { this(x, y, z, new BlockOptionalMeta(state.getBlock())); } public BlockOptionalMeta getBom() { return bom; } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { if (bom.matches(current)) { return current; } for (BlockState placeable : approxPlaceable) { if (bom.matches(placeable)) { return placeable; } } return bom.getAnyBlockState(); } } ================================================ FILE: src/api/java/baritone/api/schematic/ISchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import java.util.List; import net.minecraft.core.Direction; import net.minecraft.world.level.block.state.BlockState; /** * Basic representation of a schematic. Provides the dimensions and the desired state for a given position relative to * the origin. * * @author leijurv */ public interface ISchematic { /** * Does the block at this coordinate matter to the schematic? * <p> * Normally just a check for if the coordinate is in the cube. * <p> * However, in the case of something like a map art, anything that's below the level of the map art doesn't matter, * so this function should return false in that case. (i.e. it doesn't really have to be air below the art blocks) * * @param x The x position of the block, relative to the origin * @param y The y position of the block, relative to the origin * @param z The z position of the block, relative to the origin * @param currentState The current state of that block in the world, or null * @return Whether or not the specified position is within the bounds of this schematic */ default boolean inSchematic(int x, int y, int z, BlockState currentState) { return x >= 0 && x < widthX() && y >= 0 && y < heightY() && z >= 0 && z < lengthZ(); } default int size(Direction.Axis axis) { switch (axis) { case X: return widthX(); case Y: return heightY(); case Z: return lengthZ(); default: throw new UnsupportedOperationException(axis + ""); } } /** * Returns the desired block state at a given (X, Y, Z) position relative to the origin (0, 0, 0). * * @param x The x position of the block, relative to the origin * @param y The y position of the block, relative to the origin * @param z The z position of the block, relative to the origin * @param current The current state of that block in the world, or null * @param approxPlaceable The list of blockstates estimated to be placeable * @return The desired block state at the specified position */ BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable); /** * Resets possible caches to avoid wrong behavior when moving the schematic around */ default void reset() {} /** * @return The width (X axis length) of this schematic */ int widthX(); /** * @return The height (Y axis length) of this schematic */ int heightY(); /** * @return The length (Z axis length) of this schematic */ int lengthZ(); } ================================================ FILE: src/api/java/baritone/api/schematic/ISchematicSystem.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import baritone.api.command.registry.Registry; import baritone.api.schematic.format.ISchematicFormat; import java.io.File; import java.util.List; import java.util.Optional; /** * @author Brady * @since 12/23/2019 */ public interface ISchematicSystem { /** * @return The registry of supported schematic formats */ Registry<ISchematicFormat> getRegistry(); /** * Attempts to find an {@link ISchematicFormat} that supports the specified schematic file. * * @param file A schematic file * @return The corresponding format for the file, {@link Optional#empty()} if no candidates were found. */ Optional<ISchematicFormat> getByFile(File file); /** * @return A list of file extensions used by supported formats */ List<String> getFileExtensions(); } ================================================ FILE: src/api/java/baritone/api/schematic/IStaticSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import net.minecraft.world.level.block.state.BlockState; /** * A static schematic is capable of providing the desired state at a given position without * additional context. Schematics of this type are expected to have non-varying contents. * * @author Brady * @see #getDirect(int, int, int) * @since 12/24/2019 */ public interface IStaticSchematic extends ISchematic { /** * Gets the {@link BlockState} for a given position in this schematic. It should be guaranteed * that the return value of this method will not change given that the parameters are the same. * * @param x The X block position * @param y The Y block position * @param z The Z block position * @return The desired state at the specified position. */ BlockState getDirect(int x, int y, int z); /** * Returns an {@link BlockState} array of size {@link #heightY()} which contains all * desired block states in the specified vertical column. The index of {@link BlockState}s * in the array are equivalent to their Y position in the schematic. * * @param x The X column position * @param z The Z column position * @return An {@link BlockState} array */ default BlockState[] getColumn(int x, int z) { BlockState[] column = new BlockState[this.heightY()]; for (int i = 0; i < this.heightY(); i++) { column[i] = getDirect(x, i, z); } return column; } } ================================================ FILE: src/api/java/baritone/api/schematic/MaskSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import baritone.api.schematic.mask.Mask; import net.minecraft.world.level.block.state.BlockState; import java.util.List; public abstract class MaskSchematic extends AbstractSchematic { private final ISchematic schematic; public MaskSchematic(ISchematic schematic) { super(schematic.widthX(), schematic.heightY(), schematic.lengthZ()); this.schematic = schematic; } protected abstract boolean partOfMask(int x, int y, int z, BlockState currentState); @Override public boolean inSchematic(int x, int y, int z, BlockState currentState) { return schematic.inSchematic(x, y, z, currentState) && partOfMask(x, y, z, currentState); } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { return schematic.desiredState(x, y, z, current, approxPlaceable); } public static MaskSchematic create(ISchematic schematic, Mask function) { return new MaskSchematic(schematic) { @Override protected boolean partOfMask(int x, int y, int z, BlockState currentState) { return function.partOfMask(x, y, z, currentState); } }; } } ================================================ FILE: src/api/java/baritone/api/schematic/MirroredSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.Mirror; import java.util.List; import java.util.stream.Collectors; public class MirroredSchematic implements ISchematic { private final ISchematic schematic; private final Mirror mirror; public MirroredSchematic(ISchematic schematic, Mirror mirror) { this.schematic = schematic; this.mirror = mirror; } @Override public boolean inSchematic(int x, int y, int z, BlockState currentState) { return schematic.inSchematic( mirrorX(x, widthX(), mirror), y, mirrorZ(z, lengthZ(), mirror), mirror(currentState, mirror) ); } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { return mirror(schematic.desiredState( mirrorX(x, widthX(), mirror), y, mirrorZ(z, lengthZ(), mirror), mirror(current, mirror), mirror(approxPlaceable, mirror) ), mirror); } @Override public void reset() { schematic.reset(); } @Override public int widthX() { return schematic.widthX(); } @Override public int heightY() { return schematic.heightY(); } @Override public int lengthZ() { return schematic.lengthZ(); } private static int mirrorX(int x, int sizeX, Mirror mirror) { switch (mirror) { case NONE: case LEFT_RIGHT: return x; case FRONT_BACK: return sizeX - x - 1; } throw new IllegalArgumentException("Unknown mirror"); } private static int mirrorZ(int z, int sizeZ, Mirror mirror) { switch (mirror) { case NONE: case FRONT_BACK: return z; case LEFT_RIGHT: return sizeZ - z - 1; } throw new IllegalArgumentException("Unknown mirror"); } private static BlockState mirror(BlockState state, Mirror mirror) { if (state == null) { return null; } return state.mirror(mirror); } private static List<BlockState> mirror(List<BlockState> states, Mirror mirror) { if (states == null) { return null; } return states.stream() .map(s -> mirror(s, mirror)) .collect(Collectors.toList()); } } ================================================ FILE: src/api/java/baritone/api/schematic/ReplaceSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import baritone.api.utils.BlockOptionalMetaLookup; import net.minecraft.world.level.block.state.BlockState; public class ReplaceSchematic extends MaskSchematic { private final BlockOptionalMetaLookup filter; private final Boolean[][][] cache; public ReplaceSchematic(ISchematic schematic, BlockOptionalMetaLookup filter) { super(schematic); this.filter = filter; this.cache = new Boolean[widthX()][heightY()][lengthZ()]; } @Override public void reset() { // it's final, can't use this.cache = new Boolean[widthX()][heightY()][lengthZ()] for (int x = 0; x < cache.length; x++) { for (int y = 0; y < cache[0].length; y++) { for (int z = 0; z < cache[0][0].length; z++) { cache[x][y][z] = null; } } } } @Override protected boolean partOfMask(int x, int y, int z, BlockState currentState) { if (cache[x][y][z] == null) { cache[x][y][z] = filter.has(currentState); } return cache[x][y][z]; } } ================================================ FILE: src/api/java/baritone/api/schematic/RotatedSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.Rotation; import java.util.List; import java.util.stream.Collectors; public class RotatedSchematic implements ISchematic { private final ISchematic schematic; private final Rotation rotation; private final Rotation inverseRotation; public RotatedSchematic(ISchematic schematic, Rotation rotation) { this.schematic = schematic; this.rotation = rotation; // I don't think a 14 line switch would improve readability this.inverseRotation = rotation.getRotated(rotation).getRotated(rotation); } @Override public boolean inSchematic(int x, int y, int z, BlockState currentState) { return schematic.inSchematic( rotateX(x, z, widthX(), lengthZ(), inverseRotation), y, rotateZ(x, z, widthX(), lengthZ(), inverseRotation), rotate(currentState, inverseRotation) ); } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { return rotate(schematic.desiredState( rotateX(x, z, widthX(), lengthZ(), inverseRotation), y, rotateZ(x, z, widthX(), lengthZ(), inverseRotation), rotate(current, inverseRotation), rotate(approxPlaceable, inverseRotation) ), rotation); } @Override public void reset() { schematic.reset(); } @Override public int widthX() { return flipsCoordinates(rotation) ? schematic.lengthZ() : schematic.widthX(); } @Override public int heightY() { return schematic.heightY(); } @Override public int lengthZ() { return flipsCoordinates(rotation) ? schematic.widthX() : schematic.lengthZ(); } /** * Wether {@code rotation} swaps the x and z components */ private static boolean flipsCoordinates(Rotation rotation) { return rotation == Rotation.CLOCKWISE_90 || rotation == Rotation.COUNTERCLOCKWISE_90; } /** * The x component of x,y after applying the rotation */ private static int rotateX(int x, int z, int sizeX, int sizeZ, Rotation rotation) { switch (rotation) { case NONE: return x; case CLOCKWISE_90: return sizeZ - z - 1; case CLOCKWISE_180: return sizeX - x - 1; case COUNTERCLOCKWISE_90: return z; } throw new IllegalArgumentException("Unknown rotation"); } /** * The z component of x,y after applying the rotation */ private static int rotateZ(int x, int z, int sizeX, int sizeZ, Rotation rotation) { switch (rotation) { case NONE: return z; case CLOCKWISE_90: return x; case CLOCKWISE_180: return sizeZ - z - 1; case COUNTERCLOCKWISE_90: return sizeX - x - 1; } throw new IllegalArgumentException("Unknown rotation"); } private static BlockState rotate(BlockState state, Rotation rotation) { if (state == null) { return null; } return state.rotate(rotation); } private static List<BlockState> rotate(List<BlockState> states, Rotation rotation) { if (states == null) { return null; } return states.stream() .map(s -> rotate(s, rotation)) .collect(Collectors.toList()); } } ================================================ FILE: src/api/java/baritone/api/schematic/ShellSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import net.minecraft.world.level.block.state.BlockState; public class ShellSchematic extends MaskSchematic { public ShellSchematic(ISchematic schematic) { super(schematic); } @Override protected boolean partOfMask(int x, int y, int z, BlockState currentState) { return x == 0 || y == 0 || z == 0 || x == widthX() - 1 || y == heightY() - 1 || z == lengthZ() - 1; } } ================================================ FILE: src/api/java/baritone/api/schematic/SubstituteSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class SubstituteSchematic extends AbstractSchematic { private final ISchematic schematic; private final Map<Block, List<Block>> substitutions; private final Map<BlockState, Map<Block, BlockState>> blockStateCache = new HashMap<>(); public SubstituteSchematic(ISchematic schematic, Map<Block, List<Block>> substitutions) { super(schematic.widthX(), schematic.heightY(), schematic.lengthZ()); this.schematic = schematic; this.substitutions = substitutions; } @Override public boolean inSchematic(int x, int y, int z, BlockState currentState) { return schematic.inSchematic(x, y, z, currentState); } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { BlockState desired = schematic.desiredState(x, y, z, current, approxPlaceable); Block desiredBlock = desired.getBlock(); if (!substitutions.containsKey(desiredBlock)) { return desired; } List<Block> substitutes = substitutions.get(desiredBlock); if (substitutes.contains(current.getBlock()) && !(current.getBlock() instanceof AirBlock)) {// don't preserve air, it's almost always there and almost never wanted return withBlock(desired, current.getBlock()); } for (Block substitute : substitutes) { if (substitute instanceof AirBlock) { return current.getBlock() instanceof AirBlock ? current : Blocks.AIR.defaultBlockState(); // can always "place" air } for (BlockState placeable : approxPlaceable) { if (substitute.equals(placeable.getBlock())) { return withBlock(desired, placeable.getBlock()); } } } return substitutes.get(0).defaultBlockState(); } private BlockState withBlock(BlockState state, Block block) { if (blockStateCache.containsKey(state) && blockStateCache.get(state).containsKey(block)) { return blockStateCache.get(state).get(block); } Collection<Property<?>> properties = state.getProperties(); BlockState newState = block.defaultBlockState(); for (Property<?> property : properties) { try { newState = copySingleProp(state, newState, property); } catch (IllegalArgumentException e) { //property does not exist for target block } } blockStateCache.computeIfAbsent(state, s -> new HashMap<Block, BlockState>()).put(block, newState); return newState; } private <T extends Comparable<T>> BlockState copySingleProp(BlockState fromState, BlockState toState, Property<T> prop) { return toState.setValue(prop, fromState.getValue(prop)); } } ================================================ FILE: src/api/java/baritone/api/schematic/WallsSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic; import net.minecraft.world.level.block.state.BlockState; public class WallsSchematic extends MaskSchematic { public WallsSchematic(ISchematic schematic) { super(schematic); } @Override protected boolean partOfMask(int x, int y, int z, BlockState currentState) { return x == 0 || z == 0 || x == widthX() - 1 || z == lengthZ() - 1; } } ================================================ FILE: src/api/java/baritone/api/schematic/format/ISchematicFormat.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.format; import baritone.api.schematic.ISchematic; import baritone.api.schematic.IStaticSchematic; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * The base of a {@link ISchematic} file format * * @author Brady * @since 12/23/2019 */ public interface ISchematicFormat { /** * @return The parser for creating schematics of this format */ IStaticSchematic parse(InputStream input) throws IOException; /** * @param file The file to check against * @return Whether or not the specified file matches this schematic format */ boolean isFileType(File file); /** * @return A list of file extensions used by this format */ List<String> getFileExtensions(); } ================================================ FILE: src/api/java/baritone/api/schematic/mask/AbstractMask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask; /** * @author Brady */ public abstract class AbstractMask implements Mask { private final int widthX; private final int heightY; private final int lengthZ; public AbstractMask(int widthX, int heightY, int lengthZ) { this.widthX = widthX; this.heightY = heightY; this.lengthZ = lengthZ; } @Override public int widthX() { return this.widthX; } @Override public int heightY() { return this.heightY; } @Override public int lengthZ() { return this.lengthZ; } } ================================================ FILE: src/api/java/baritone/api/schematic/mask/Mask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask; import baritone.api.schematic.mask.operator.BinaryOperatorMask; import baritone.api.schematic.mask.operator.NotMask; import baritone.api.utils.BooleanBinaryOperators; import net.minecraft.world.level.block.state.BlockState; /** * @author Brady */ public interface Mask { /** * @param x The relative x position of the block * @param y The relative y position of the block * @param z The relative z position of the block * @param currentState The current state of that block in the world, may be {@code null} * @return Whether the given position is included in this mask */ boolean partOfMask(int x, int y, int z, BlockState currentState); int widthX(); int heightY(); int lengthZ(); default Mask not() { return new NotMask(this); } default Mask union(Mask other) { return new BinaryOperatorMask(this, other, BooleanBinaryOperators.OR); } default Mask intersection(Mask other) { return new BinaryOperatorMask(this, other, BooleanBinaryOperators.AND); } default Mask xor(Mask other) { return new BinaryOperatorMask(this, other, BooleanBinaryOperators.XOR); } } ================================================ FILE: src/api/java/baritone/api/schematic/mask/PreComputedMask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask; /** * @author Brady */ final class PreComputedMask extends AbstractMask implements StaticMask { private final boolean[][][] mask; public PreComputedMask(StaticMask mask) { super(mask.widthX(), mask.heightY(), mask.lengthZ()); this.mask = new boolean[this.heightY()][this.lengthZ()][this.widthX()]; for (int y = 0; y < this.heightY(); y++) { for (int z = 0; z < this.lengthZ(); z++) { for (int x = 0; x < this.widthX(); x++) { this.mask[y][z][x] = mask.partOfMask(x, y, z); } } } } @Override public boolean partOfMask(int x, int y, int z) { return this.mask[y][z][x]; } } ================================================ FILE: src/api/java/baritone/api/schematic/mask/StaticMask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask; import baritone.api.schematic.mask.operator.BinaryOperatorMask; import baritone.api.schematic.mask.operator.NotMask; import baritone.api.utils.BooleanBinaryOperators; import net.minecraft.world.level.block.state.BlockState; /** * A mask that is context-free. In other words, it doesn't require the current block state to determine if a relative * position is a part of the mask. * * @author Brady */ public interface StaticMask extends Mask { /** * Determines if a given relative coordinate is included in this mask, without the need for the current block state. * * @param x The relative x position of the block * @param y The relative y position of the block * @param z The relative z position of the block * @return Whether the given position is included in this mask */ boolean partOfMask(int x, int y, int z); /** * Implements the parent {@link Mask#partOfMask partOfMask function} by calling the static function * provided in this functional interface without needing the {@link BlockState} argument. This {@code default} * implementation should <b><u>NOT</u></b> be overriden. * * @param x The relative x position of the block * @param y The relative y position of the block * @param z The relative z position of the block * @param currentState The current state of that block in the world, may be {@code null} * @return Whether the given position is included in this mask */ @Override default boolean partOfMask(int x, int y, int z, BlockState currentState) { return this.partOfMask(x, y, z); } @Override default StaticMask not() { return new NotMask.Static(this); } default StaticMask union(StaticMask other) { return new BinaryOperatorMask.Static(this, other, BooleanBinaryOperators.OR); } default StaticMask intersection(StaticMask other) { return new BinaryOperatorMask.Static(this, other, BooleanBinaryOperators.AND); } default StaticMask xor(StaticMask other) { return new BinaryOperatorMask.Static(this, other, BooleanBinaryOperators.XOR); } /** * Returns a pre-computed mask using {@code this} function, with the specified size parameters. */ default StaticMask compute() { return new PreComputedMask(this); } } ================================================ FILE: src/api/java/baritone/api/schematic/mask/operator/BinaryOperatorMask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask.operator; import baritone.api.schematic.mask.AbstractMask; import baritone.api.schematic.mask.Mask; import baritone.api.schematic.mask.StaticMask; import baritone.api.utils.BooleanBinaryOperator; import net.minecraft.world.level.block.state.BlockState; /** * @author Brady */ public final class BinaryOperatorMask extends AbstractMask { private final Mask a; private final Mask b; private final BooleanBinaryOperator operator; public BinaryOperatorMask(Mask a, Mask b, BooleanBinaryOperator operator) { super(Math.max(a.widthX(), b.widthX()), Math.max(a.heightY(), b.heightY()), Math.max(a.lengthZ(), b.lengthZ())); this.a = a; this.b = b; this.operator = operator; } @Override public boolean partOfMask(int x, int y, int z, BlockState currentState) { return this.operator.applyAsBoolean( partOfMask(a, x, y, z, currentState), partOfMask(b, x, y, z, currentState) ); } private static boolean partOfMask(Mask mask, int x, int y, int z, BlockState currentState) { return x < mask.widthX() && y < mask.heightY() && z < mask.lengthZ() && mask.partOfMask(x, y, z, currentState); } public static final class Static extends AbstractMask implements StaticMask { private final StaticMask a; private final StaticMask b; private final BooleanBinaryOperator operator; public Static(StaticMask a, StaticMask b, BooleanBinaryOperator operator) { super(Math.max(a.widthX(), b.widthX()), Math.max(a.heightY(), b.heightY()), Math.max(a.lengthZ(), b.lengthZ())); this.a = a; this.b = b; this.operator = operator; } @Override public boolean partOfMask(int x, int y, int z) { return this.operator.applyAsBoolean( partOfMask(a, x, y, z), partOfMask(b, x, y, z) ); } private static boolean partOfMask(StaticMask mask, int x, int y, int z) { return x < mask.widthX() && y < mask.heightY() && z < mask.lengthZ() && mask.partOfMask(x, y, z); } } } ================================================ FILE: src/api/java/baritone/api/schematic/mask/operator/NotMask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask.operator; import baritone.api.schematic.mask.AbstractMask; import baritone.api.schematic.mask.Mask; import baritone.api.schematic.mask.StaticMask; import net.minecraft.world.level.block.state.BlockState; /** * @author Brady */ public final class NotMask extends AbstractMask { private final Mask source; public NotMask(Mask source) { super(source.widthX(), source.heightY(), source.lengthZ()); this.source = source; } @Override public boolean partOfMask(int x, int y, int z, BlockState currentState) { return !this.source.partOfMask(x, y, z, currentState); } public static final class Static extends AbstractMask implements StaticMask { private final StaticMask source; public Static(StaticMask source) { super(source.widthX(), source.heightY(), source.lengthZ()); this.source = source; } @Override public boolean partOfMask(int x, int y, int z) { return !this.source.partOfMask(x, y, z); } } } ================================================ FILE: src/api/java/baritone/api/schematic/mask/shape/CylinderMask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask.shape; import baritone.api.schematic.mask.AbstractMask; import baritone.api.schematic.mask.StaticMask; import net.minecraft.core.Direction; /** * @author Brady */ public final class CylinderMask extends AbstractMask implements StaticMask { private final double centerA; private final double centerB; private final double radiusSqA; private final double radiusSqB; private final boolean filled; private final Direction.Axis alignment; public CylinderMask(int widthX, int heightY, int lengthZ, boolean filled, Direction.Axis alignment) { super(widthX, heightY, lengthZ); this.centerA = this.getA(widthX, heightY, alignment) / 2.0; this.centerB = this.getB(heightY, lengthZ, alignment) / 2.0; this.radiusSqA = (this.centerA - 1) * (this.centerA - 1); this.radiusSqB = (this.centerB - 1) * (this.centerB - 1); this.filled = filled; this.alignment = alignment; } @Override public boolean partOfMask(int x, int y, int z) { double da = Math.abs((this.getA(x, y, this.alignment) + 0.5) - this.centerA); double db = Math.abs((this.getB(y, z, this.alignment) + 0.5) - this.centerB); if (this.outside(da, db)) { return false; } return this.filled || this.outside(da + 1, db) || this.outside(da, db + 1); } private boolean outside(double da, double db) { return da * da / this.radiusSqA + db * db / this.radiusSqB > 1; } private static int getA(int x, int y, Direction.Axis alignment) { return alignment == Direction.Axis.X ? y : x; } private static int getB(int y, int z, Direction.Axis alignment) { return alignment == Direction.Axis.Z ? y : z; } } ================================================ FILE: src/api/java/baritone/api/schematic/mask/shape/SphereMask.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.schematic.mask.shape; import baritone.api.schematic.mask.AbstractMask; import baritone.api.schematic.mask.StaticMask; /** * @author Brady */ public final class SphereMask extends AbstractMask implements StaticMask { private final double centerX; private final double centerY; private final double centerZ; private final double radiusSqX; private final double radiusSqY; private final double radiusSqZ; private final boolean filled; public SphereMask(int widthX, int heightY, int lengthZ, boolean filled) { super(widthX, heightY, lengthZ); this.centerX = widthX / 2.0; this.centerY = heightY / 2.0; this.centerZ = lengthZ / 2.0; this.radiusSqX = this.centerX * this.centerX; this.radiusSqY = this.centerY * this.centerY; this.radiusSqZ = this.centerZ * this.centerZ; this.filled = filled; } @Override public boolean partOfMask(int x, int y, int z) { double dx = Math.abs((x + 0.5) - this.centerX); double dy = Math.abs((y + 0.5) - this.centerY); double dz = Math.abs((z + 0.5) - this.centerZ); if (this.outside(dx, dy, dz)) { return false; } return this.filled || this.outside(dx + 1, dy, dz) || this.outside(dx, dy + 1, dz) || this.outside(dx, dy, dz + 1); } private boolean outside(double dx, double dy, double dz) { return dx * dx / this.radiusSqX + dy * dy / this.radiusSqY + dz * dz / this.radiusSqZ > 1; } } ================================================ FILE: src/api/java/baritone/api/selection/ISelection.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.selection; import baritone.api.utils.BetterBlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.world.phys.AABB; /** * A selection is an immutable object representing the current selection. The selection is commonly used for certain * types of build commands, however it can be used for anything. */ public interface ISelection { /** * @return The first corner of this selection. This is meant to preserve the user's original first corner. */ BetterBlockPos pos1(); /** * @return The second corner of this selection. This is meant to preserve the user's original second corner. */ BetterBlockPos pos2(); /** * @return The {@link BetterBlockPos} with the lowest x, y, and z position in the selection. */ BetterBlockPos min(); /** * @return The opposite corner from the {@link #min()}. */ BetterBlockPos max(); /** * @return The size of this ISelection. */ Vec3i size(); /** * @return An {@link AABB} encompassing all blocks in this selection. */ AABB aabb(); /** * Returns a new {@link ISelection} expanded in the specified direction by the specified number of blocks. * * @param direction The direction to expand the selection. * @param blocks How many blocks to expand it. * @return A new selection, expanded as specified. */ ISelection expand(Direction direction, int blocks); /** * Returns a new {@link ISelection} contracted in the specified direction by the specified number of blocks. * <p> * Note that, for example, if the direction specified is UP, the bottom of the selection will be shifted up. If it * is DOWN, the top of the selection will be shifted down. * * @param direction The direction to contract the selection. * @param blocks How many blocks to contract it. * @return A new selection, contracted as specified. */ ISelection contract(Direction direction, int blocks); /** * Returns a new {@link ISelection} shifted in the specified direction by the specified number of blocks. This moves * the whole selection. * * @param direction The direction to shift the selection. * @param blocks How many blocks to shift it. * @return A new selection, shifted as specified. */ ISelection shift(Direction direction, int blocks); } ================================================ FILE: src/api/java/baritone/api/selection/ISelectionManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.selection; import baritone.api.utils.BetterBlockPos; import net.minecraft.core.Direction; /** * The selection manager handles setting Baritone's selections. You can set the selection here, as well as retrieving * the current selection. */ public interface ISelectionManager { /** * Adds a new selection. The added selection is returned. * * @param selection The new selection to add. */ ISelection addSelection(ISelection selection); /** * Adds a new {@link ISelection} constructed from the given block positions. The new selection is returned. * * @param pos1 One corner of the selection * @param pos2 The new corner of the selection */ ISelection addSelection(BetterBlockPos pos1, BetterBlockPos pos2); /** * Removes the selection from the current selections. * * @param selection The selection to remove. * @return The removed selection. */ ISelection removeSelection(ISelection selection); /** * Removes all selections. * * @return The selections that were removed, sorted from oldest to newest.. */ ISelection[] removeAllSelections(); /** * @return The current selections, sorted from oldest to newest. */ ISelection[] getSelections(); /** * For anything expecting only one selection, this method is provided. However, to enforce multi-selection support, * this method will only return a selection if there is ONLY one. * * @return The only selection, or null if there isn't only one. */ ISelection getOnlySelection(); /** * This method will always return the last selection. ONLY use this if you want to, for example, modify the most * recent selection based on user input. ALWAYS use {@link #getOnlySelection()} or, ideally, * {@link #getSelections()} for retrieving the content of selections. * * @return The last selection, or null if it doesn't exist. */ ISelection getLastSelection(); /** * Replaces the specified {@link ISelection} with one expanded in the specified direction by the specified number of * blocks. Returns the new selection. * * @param selection The selection to expand. * @param direction The direction to expand the selection. * @param blocks How many blocks to expand it. * @return The new selection, expanded as specified. */ ISelection expand(ISelection selection, Direction direction, int blocks); /** * Replaces the specified {@link ISelection} with one contracted in the specified direction by the specified number * of blocks. * <p> * Note that, for example, if the direction specified is UP, the bottom of the selection will be shifted up. If it * is DOWN, the top of the selection will be shifted down. * * @param selection The selection to contract. * @param direction The direction to contract the selection. * @param blocks How many blocks to contract it. * @return The new selection, contracted as specified. */ ISelection contract(ISelection selection, Direction direction, int blocks); /** * Replaces the specified {@link ISelection} with one shifted in the specified direction by the specified number of * blocks. This moves the whole selection. * * @param selection The selection to shift. * @param direction The direction to shift the selection. * @param blocks How many blocks to shift it. * @return The new selection, shifted as specified. */ ISelection shift(ISelection selection, Direction direction, int blocks); } ================================================ FILE: src/api/java/baritone/api/utils/BetterBlockPos.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import javax.annotation.Nonnull; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.util.Mth; /** * A better BlockPos that has fewer hash collisions (and slightly more performant offsets) * <p> * Is it really faster to subclass BlockPos and calculate a hash in the constructor like this, taking everything into account? * Yes. 20% faster actually. It's called BETTER BlockPos for a reason. Source: * <a href="https://docs.google.com/spreadsheets/d/1GWjOjOZINkg_0MkRgKRPH1kUzxjsnEROD9u3UFh_DJc">Benchmark Spreadsheet</a> * * @author leijurv */ public final class BetterBlockPos extends BlockPos { private static final int NUM_X_BITS = 26; private static final int NUM_Z_BITS = NUM_X_BITS; private static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS; private static final int Y_SHIFT = NUM_Z_BITS; private static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS; private static final long X_MASK = (1L << NUM_X_BITS) - 1L; private static final long Y_MASK = (1L << NUM_Y_BITS) - 1L; private static final long Z_MASK = (1L << NUM_Z_BITS) - 1L; public static final BetterBlockPos ORIGIN = new BetterBlockPos(0, 0, 0); public final int x; public final int y; public final int z; public BetterBlockPos(int x, int y, int z) { super(x, y, z); this.x = x; this.y = y; this.z = z; } public BetterBlockPos(double x, double y, double z) { this(Mth.floor(x), Mth.floor(y), Mth.floor(z)); } public BetterBlockPos(BlockPos pos) { this(pos.getX(), pos.getY(), pos.getZ()); } /** * Like constructor but returns null if pos is null, good if you just need to possibly censor coordinates * * @param pos The BlockPos, possibly null, to convert * @return A BetterBlockPos or null if pos was null */ public static BetterBlockPos from(BlockPos pos) { if (pos == null) { return null; } return new BetterBlockPos(pos); } @Override public int hashCode() { return (int) longHash(x, y, z); } public static long longHash(BetterBlockPos pos) { return longHash(pos.x, pos.y, pos.z); } public static long longHash(int x, int y, int z) { // TODO use the same thing as BlockPos.fromLong(); // invertibility would be incredibly useful /* * This is the hashcode implementation of Vec3i (the superclass of the class which I shall not name) * * public int hashCode() { * return (this.getY() + this.getZ() * 31) * 31 + this.getX(); * } * * That is terrible and has tons of collisions and makes the HashMap terribly inefficient. * * That's why we grab out the X, Y, Z and calculate our own hashcode */ long hash = 3241; hash = 3457689L * hash + x; hash = 8734625L * hash + y; hash = 2873465L * hash + z; return hash; } @Override public boolean equals(Object o) { if (o == null) { return false; } if (o instanceof BetterBlockPos) { BetterBlockPos oth = (BetterBlockPos) o; return oth.x == x && oth.y == y && oth.z == z; } // during path execution, like "if (whereShouldIBe.equals(whereAmI)) {" // sometimes we compare a BlockPos to a BetterBlockPos BlockPos oth = (BlockPos) o; return oth.getX() == x && oth.getY() == y && oth.getZ() == z; } @Override public BetterBlockPos above() { // this is unimaginably faster than blockpos.up // that literally calls // this.up(1) // which calls this.offset(Direction.UP, 1) // which does return n == 0 ? this : new BlockPos(this.getX() + facing.getXOffset() * n, this.getY() + facing.getYOffset() * n, this.getZ() + facing.getZOffset() * n); // how many function calls is that? up(), up(int), offset(Direction, int), new BlockPos, getX, getXOffset, getY, getYOffset, getZ, getZOffset // that's ten. // this is one function call. return new BetterBlockPos(x, y + 1, z); } @Override public BetterBlockPos above(int amt) { // see comment in up() return amt == 0 ? this : new BetterBlockPos(x, y + amt, z); } @Override public BetterBlockPos below() { // see comment in up() return new BetterBlockPos(x, y - 1, z); } @Override public BetterBlockPos below(int amt) { // see comment in up() return amt == 0 ? this : new BetterBlockPos(x, y - amt, z); } @Override public BetterBlockPos relative(Direction dir) { Vec3i vec = dir.getNormal(); return new BetterBlockPos(x + vec.getX(), y + vec.getY(), z + vec.getZ()); } @Override public BetterBlockPos relative(Direction dir, int dist) { if (dist == 0) { return this; } Vec3i vec = dir.getNormal(); return new BetterBlockPos(x + vec.getX() * dist, y + vec.getY() * dist, z + vec.getZ() * dist); } @Override public BetterBlockPos north() { return new BetterBlockPos(x, y, z - 1); } @Override public BetterBlockPos north(int amt) { return amt == 0 ? this : new BetterBlockPos(x, y, z - amt); } @Override public BetterBlockPos south() { return new BetterBlockPos(x, y, z + 1); } @Override public BetterBlockPos south(int amt) { return amt == 0 ? this : new BetterBlockPos(x, y, z + amt); } @Override public BetterBlockPos east() { return new BetterBlockPos(x + 1, y, z); } @Override public BetterBlockPos east(int amt) { return amt == 0 ? this : new BetterBlockPos(x + amt, y, z); } @Override public BetterBlockPos west() { return new BetterBlockPos(x - 1, y, z); } @Override public BetterBlockPos west(int amt) { return amt == 0 ? this : new BetterBlockPos(x - amt, y, z); } public double distanceSq(final BetterBlockPos to) { double dx = (double) this.x - to.x; double dy = (double) this.y - to.y; double dz = (double) this.z - to.z; return dx * dx + dy * dy + dz * dz; } public double distanceTo(final BetterBlockPos to) { double dx = (double) this.x - to.x; double dy = (double) this.y - to.y; double dz = (double) this.z - to.z; return Math.sqrt(dx * dx + dy * dy + dz * dz); } @Override @Nonnull public String toString() { return String.format( "BetterBlockPos{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } public static long serializeToLong(final int x, final int y, final int z) { return ((long) x & X_MASK) << X_SHIFT | ((long) y & Y_MASK) << Y_SHIFT | ((long) z & Z_MASK); } public static BetterBlockPos deserializeFromLong(final long serialized) { final int x = (int) (serialized << 64 - X_SHIFT - NUM_X_BITS >> 64 - NUM_X_BITS); final int y = (int) (serialized << 64 - Y_SHIFT - NUM_Y_BITS >> 64 - NUM_Y_BITS); final int z = (int) (serialized << 64 - NUM_Z_BITS >> 64 - NUM_Z_BITS); return new BetterBlockPos(x, y, z); } } ================================================ FILE: src/api/java/baritone/api/utils/BlockOptionalMeta.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.utils.accessor.IItemStack; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import io.netty.util.concurrent.ThreadPerTaskExecutor; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.packs.*; import net.minecraft.server.packs.repository.ServerPacksSource; import net.minecraft.server.packs.resources.MultiPackResourceManager; import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.util.RandomSource; import net.minecraft.util.Unit; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.CustomSpawner; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.level.storage.loot.BuiltInLootTables; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.LootTables; import net.minecraft.world.level.storage.loot.PredicateManager; import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; import net.minecraft.world.phys.Vec3; import sun.misc.Unsafe; import javax.annotation.Nonnull; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public final class BlockOptionalMeta { // id or id[] or id[properties] where id and properties are any text with at least one character private static final Pattern PATTERN = Pattern.compile("^(?<id>.+?)(?:\\[(?<properties>.+?)?\\])?$"); private final Block block; private final String propertiesDescription; // exists so toString() can return something more useful than a list of all blockstates private final Set<BlockState> blockstates; private final Set<Integer> stateHashes; private final Set<Integer> stackHashes; private static LootTables lootTables; private static PredicateManager predicate = new PredicateManager(); private static Map<Block, List<Item>> drops = new HashMap<>(); public BlockOptionalMeta(@Nonnull Block block) { this.block = block; this.propertiesDescription = "{}"; this.blockstates = getStates(block, Collections.emptyMap()); this.stateHashes = getStateHashes(blockstates); this.stackHashes = getStackHashes(blockstates); } public BlockOptionalMeta(@Nonnull String selector) { Matcher matcher = PATTERN.matcher(selector); if (!matcher.find()) { throw new IllegalArgumentException("invalid block selector"); } block = BlockUtils.stringToBlockRequired(matcher.group("id")); String props = matcher.group("properties"); Map<Property<?>, ?> properties = props == null || props.equals("") ? Collections.emptyMap() : parseProperties(block, props); propertiesDescription = props == null ? "{}" : "{" + props.replace("=", ":") + "}"; blockstates = getStates(block, properties); stateHashes = getStateHashes(blockstates); stackHashes = getStackHashes(blockstates); } private static <C extends Comparable<C>, P extends Property<C>> P castToIProperty(Object value) { //noinspection unchecked return (P) value; } private static Map<Property<?>, ?> parseProperties(Block block, String raw) { ImmutableMap.Builder<Property<?>, Object> builder = ImmutableMap.builder(); for (String pair : raw.split(",")) { String[] parts = pair.split("="); if (parts.length != 2) { throw new IllegalArgumentException(String.format("\"%s\" is not a valid property-value pair", pair)); } String rawKey = parts[0]; String rawValue = parts[1]; Property<?> key = block.getStateDefinition().getProperty(rawKey); Comparable<?> value = castToIProperty(key).getValue(rawValue) .orElseThrow(() -> new IllegalArgumentException(String.format( "\"%s\" is not a valid value for %s on %s", rawValue, key, block ))); builder.put(key, value); } return builder.build(); } private static Set<BlockState> getStates(@Nonnull Block block, @Nonnull Map<Property<?>, ?> properties) { return block.getStateDefinition().getPossibleStates().stream() .filter(blockstate -> properties.entrySet().stream().allMatch(entry -> blockstate.getValue(entry.getKey()) == entry.getValue() )) .collect(Collectors.toSet()); } private static ImmutableSet<Integer> getStateHashes(Set<BlockState> blockstates) { return ImmutableSet.copyOf( blockstates.stream() .map(BlockState::hashCode) .toArray(Integer[]::new) ); } private static ImmutableSet<Integer> getStackHashes(Set<BlockState> blockstates) { //noinspection ConstantConditions return ImmutableSet.copyOf( blockstates.stream() .flatMap(state -> drops(state.getBlock()) .stream() .map(item -> new ItemStack(item, 1)) ) .map(stack -> ((IItemStack) (Object) stack).getBaritoneHash()) .toArray(Integer[]::new) ); } public Block getBlock() { return block; } public boolean matches(@Nonnull Block block) { return block == this.block; } public boolean matches(@Nonnull BlockState blockstate) { Block block = blockstate.getBlock(); return block == this.block && stateHashes.contains(blockstate.hashCode()); } public boolean matches(ItemStack stack) { //noinspection ConstantConditions int hash = ((IItemStack) (Object) stack).getBaritoneHash(); hash -= stack.getDamageValue(); return stackHashes.contains(hash); } @Override public String toString() { return String.format("BlockOptionalMeta{block=%s,properties=%s}", block, propertiesDescription); } public BlockState getAnyBlockState() { if (blockstates.size() > 0) { return blockstates.iterator().next(); } return null; } public Set<BlockState> getAllBlockStates() { return blockstates; } public Set<Integer> stackHashes() { return stackHashes; } private static Method getVanillaServerPack; private static VanillaPackResources getVanillaServerPack() { if (getVanillaServerPack == null) { getVanillaServerPack = Arrays.stream(ServerPacksSource.class.getDeclaredMethods()).filter(field -> field.getReturnType() == VanillaPackResources.class).findFirst().orElseThrow(); getVanillaServerPack.setAccessible(true); } try { return (VanillaPackResources) getVanillaServerPack.invoke(null); } catch (Exception e) { e.printStackTrace(); } return null; } public static LootTables getManager() { if (lootTables == null) { MultiPackResourceManager resources = new MultiPackResourceManager(PackType.SERVER_DATA, List.of(getVanillaServerPack())); ReloadableResourceManager resourceManager = new ReloadableResourceManager(PackType.SERVER_DATA); lootTables = new LootTables(predicate); resourceManager.registerReloadListener(lootTables); try { resourceManager.createReload(new ThreadPerTaskExecutor(Thread::new), new ThreadPerTaskExecutor(Thread::new), CompletableFuture.completedFuture(Unit.INSTANCE), resources.listPacks().toList()).done().get(); } catch (Exception exception) { throw new RuntimeException(exception); } } return lootTables; } public static PredicateManager getPredicateManager() { return predicate; } private static synchronized List<Item> drops(Block b) { return drops.computeIfAbsent(b, block -> { ResourceLocation lootTableLocation = block.getLootTable(); if (lootTableLocation == BuiltInLootTables.EMPTY) { return Collections.emptyList(); } else { List<Item> items = new ArrayList<>(); try { getManager().get(lootTableLocation).getRandomItems( new LootContext.Builder(ServerLevelStub.fastCreate()) .withRandom(RandomSource.create()) .withParameter(LootContextParams.ORIGIN, Vec3.atLowerCornerOf(BlockPos.ZERO)) .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) .withOptionalParameter(LootContextParams.BLOCK_ENTITY, null) .withParameter(LootContextParams.BLOCK_STATE, block.defaultBlockState()) .create(LootContextParamSets.BLOCK), stack -> items.add(stack.getItem()) ); } catch (Exception e) { e.printStackTrace(); } return items; } }); } private static class ServerLevelStub extends ServerLevel { private static Minecraft client = Minecraft.getInstance(); private static Unsafe unsafe = getUnsafe(); public ServerLevelStub(MinecraftServer $$0, Executor $$1, LevelStorageSource.LevelStorageAccess $$2, ServerLevelData $$3, ResourceKey<Level> $$4, LevelStem $$5, ChunkProgressListener $$6, boolean $$7, long $$8, List<CustomSpawner> $$9, boolean $$10) { super($$0, $$1, $$2, $$3, $$4, $$5, $$6, $$7, $$8, $$9, $$10); } @Override public FeatureFlagSet enabledFeatures() { assert client.level != null; return client.level.enabledFeatures(); } public static ServerLevelStub fastCreate() { try { return (ServerLevelStub) unsafe.allocateInstance(ServerLevelStub.class); } catch (InstantiationException e) { throw new RuntimeException(e); } } public static Unsafe getUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(null); } catch (Exception e) { throw new RuntimeException(e); } } } } ================================================ FILE: src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.utils.accessor.IItemStack; import com.google.common.collect.ImmutableSet; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Stream; public class BlockOptionalMetaLookup { private final ImmutableSet<Block> blockSet; private final ImmutableSet<BlockState> blockStateSet; private final ImmutableSet<Integer> stackHashes; private final BlockOptionalMeta[] boms; public BlockOptionalMetaLookup(BlockOptionalMeta... boms) { this.boms = boms; Set<Block> blocks = new HashSet<>(); Set<BlockState> blockStates = new HashSet<>(); Set<Integer> stacks = new HashSet<>(); for (BlockOptionalMeta bom : boms) { blocks.add(bom.getBlock()); blockStates.addAll(bom.getAllBlockStates()); stacks.addAll(bom.stackHashes()); } this.blockSet = ImmutableSet.copyOf(blocks); this.blockStateSet = ImmutableSet.copyOf(blockStates); this.stackHashes = ImmutableSet.copyOf(stacks); } public BlockOptionalMetaLookup(Block... blocks) { this(Stream.of(blocks) .map(BlockOptionalMeta::new) .toArray(BlockOptionalMeta[]::new)); } public BlockOptionalMetaLookup(List<Block> blocks) { this(blocks.stream() .map(BlockOptionalMeta::new) .toArray(BlockOptionalMeta[]::new)); } public BlockOptionalMetaLookup(String... blocks) { this(Stream.of(blocks) .map(BlockOptionalMeta::new) .toArray(BlockOptionalMeta[]::new)); } public boolean has(Block block) { return blockSet.contains(block); } public boolean has(BlockState state) { return blockStateSet.contains(state); } public boolean has(ItemStack stack) { int hash = ((IItemStack) (Object) stack).getBaritoneHash(); hash -= stack.getDamageValue(); return stackHashes.contains(hash); } public List<BlockOptionalMeta> blocks() { return Arrays.asList(boms); } @Override public String toString() { return String.format( "BlockOptionalMetaLookup{%s}", Arrays.toString(boms) ); } } ================================================ FILE: src/api/java/baritone/api/utils/BlockUtils.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import java.util.HashMap; import java.util.Map; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; public class BlockUtils { private static transient Map<String, Block> resourceCache = new HashMap<>(); public static String blockToString(Block block) { ResourceLocation loc = BuiltInRegistries.BLOCK.getKey(block); String name = loc.getPath(); // normally, only write the part after the minecraft: if (!loc.getNamespace().equals("minecraft")) { // Baritone is running on top of forge with mods installed, perhaps? name = loc.toString(); // include the namespace with the colon } return name; } public static Block stringToBlockRequired(String name) { Block block = stringToBlockNullable(name); if (block == null) { throw new IllegalArgumentException(String.format("Invalid block name %s", name)); } return block; } public static Block stringToBlockNullable(String name) { // do NOT just replace this with a computeWithAbsent, it isn't thread safe Block block = resourceCache.get(name); // map is never mutated in place so this is safe if (block != null) { return block; } if (resourceCache.containsKey(name)) { return null; // cached as null } block = BuiltInRegistries.BLOCK.getOptional(ResourceLocation.tryParse(name.contains(":") ? name : "minecraft:" + name)).orElse(null); Map<String, Block> copy = new HashMap<>(resourceCache); // read only copy is safe, wont throw concurrentmodification copy.put(name, block); resourceCache = copy; return block; } private BlockUtils() {} } ================================================ FILE: src/api/java/baritone/api/utils/BooleanBinaryOperator.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; /** * @author Brady */ @FunctionalInterface public interface BooleanBinaryOperator { boolean applyAsBoolean(boolean a, boolean b); } ================================================ FILE: src/api/java/baritone/api/utils/BooleanBinaryOperators.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; /** * @author Brady */ public enum BooleanBinaryOperators implements BooleanBinaryOperator { OR((a, b) -> a || b), AND((a, b) -> a && b), XOR((a, b) -> a ^ b); private final BooleanBinaryOperator op; BooleanBinaryOperators(BooleanBinaryOperator op) { this.op = op; } @Override public boolean applyAsBoolean(boolean a, boolean b) { return this.op.applyAsBoolean(a, b); } } ================================================ FILE: src/api/java/baritone/api/utils/Helper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.BaritoneAPI; import baritone.api.Settings; import net.minecraft.ChatFormatting; import net.minecraft.client.GuiMessageTag; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import java.util.Arrays; import java.util.Calendar; import java.util.stream.Stream; /** * An ease-of-access interface to provide the {@link Minecraft} game instance, * chat and console logging mechanisms, and the Baritone chat prefix. * * @author Brady * @since 8/1/2018 */ public interface Helper { /** * Instance of {@link Helper}. Used for static-context reference. */ Helper HELPER = new Helper() {}; /** * The main game instance returned by {@link Minecraft#getInstance()}. * Deprecated since {@link IPlayerContext#minecraft()} should be used instead (In the majority of cases). */ @Deprecated Minecraft mc = Minecraft.getInstance(); /** * The tag to assign to chat messages when {@link Settings#useMessageTag} is {@code true}. */ GuiMessageTag MESSAGE_TAG = new GuiMessageTag(0xFF55FF, null, Component.literal("Baritone message."), "Baritone"); static Component getPrefix() { // Inner text component final Calendar now = Calendar.getInstance(); final boolean xd = now.get(Calendar.MONTH) == Calendar.APRIL && now.get(Calendar.DAY_OF_MONTH) <= 3; MutableComponent baritone = Component.literal(xd ? "Baritoe" : BaritoneAPI.getSettings().shortBaritonePrefix.value ? "B" : "Baritone"); baritone.setStyle(baritone.getStyle().withColor(ChatFormatting.LIGHT_PURPLE)); // Outer brackets MutableComponent prefix = Component.literal(""); prefix.setStyle(baritone.getStyle().withColor(ChatFormatting.DARK_PURPLE)); prefix.append("["); prefix.append(baritone); prefix.append("]"); return prefix; } /** * Send a message to display as a toast popup * * @param title The title to display in the popup * @param message The message to display in the popup */ default void logToast(Component title, Component message) { Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().toaster.value.accept(title, message)); } /** * Send a message to display as a toast popup * * @param title The title to display in the popup * @param message The message to display in the popup */ default void logToast(String title, String message) { logToast(Component.literal(title), Component.literal(message)); } /** * Send a message to display as a toast popup * * @param message The message to display in the popup */ default void logToast(String message) { logToast(Helper.getPrefix(), Component.literal(message)); } /** * Send a message as a desktop notification * * @param message The message to display in the notification */ default void logNotification(String message) { logNotification(message, false); } /** * Send a message as a desktop notification * * @param message The message to display in the notification * @param error Whether to log as an error */ default void logNotification(String message, boolean error) { if (BaritoneAPI.getSettings().desktopNotifications.value) { logNotificationDirect(message, error); } } /** * Send a message as a desktop notification regardless of desktopNotifications * (should only be used for critically important messages) * * @param message The message to display in the notification */ default void logNotificationDirect(String message) { logNotificationDirect(message, false); } /** * Send a message as a desktop notification regardless of desktopNotifications * (should only be used for critically important messages) * * @param message The message to display in the notification * @param error Whether to log as an error */ default void logNotificationDirect(String message, boolean error) { Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().notifier.value.accept(message, error)); } /** * Send a message to chat only if chatDebug is on * * @param message The message to display in chat */ default void logDebug(String message) { if (!BaritoneAPI.getSettings().chatDebug.value) { //System.out.println("Suppressed debug message:"); //System.out.println(message); return; } // We won't log debug chat into toasts // Because only a madman would want that extreme spam -_- logDirect(message, false); } /** * Send components to chat with the [Baritone] prefix * * @param logAsToast Whether to log as a toast notification * @param components The components to send */ default void logDirect(boolean logAsToast, Component... components) { MutableComponent component = Component.literal(""); if (!logAsToast && !BaritoneAPI.getSettings().useMessageTag.value) { component.append(getPrefix()); component.append(Component.literal(" ")); } Arrays.asList(components).forEach(component::append); if (logAsToast) { logToast(getPrefix(), component); } else { Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().logger.value.accept(component)); } } /** * Send components to chat with the [Baritone] prefix * * @param components The components to send */ default void logDirect(Component... components) { logDirect(BaritoneAPI.getSettings().logAsToast.value, components); } /** * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a * direct response to a chat command) * * @param message The message to display in chat * @param color The color to print that message in * @param logAsToast Whether to log as a toast notification */ default void logDirect(String message, ChatFormatting color, boolean logAsToast) { Stream.of(message.split("\n")).forEach(line -> { MutableComponent component = Component.literal(line.replace("\t", " ")); component.setStyle(component.getStyle().withColor(color)); logDirect(logAsToast, component); }); } /** * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a * direct response to a chat command) * * @param message The message to display in chat * @param color The color to print that message in */ default void logDirect(String message, ChatFormatting color) { logDirect(message, color, BaritoneAPI.getSettings().logAsToast.value); } /** * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a * direct response to a chat command) * * @param message The message to display in chat * @param logAsToast Whether to log as a toast notification */ default void logDirect(String message, boolean logAsToast) { logDirect(message, ChatFormatting.GRAY, logAsToast); } /** * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a * direct response to a chat command) * * @param message The message to display in chat */ default void logDirect(String message) { logDirect(message, BaritoneAPI.getSettings().logAsToast.value); } default void logUnhandledException(final Throwable exception) { HELPER.logDirect("An unhandled exception occurred. " + "The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues", ChatFormatting.RED); exception.printStackTrace(); } } ================================================ FILE: src/api/java/baritone/api/utils/IInputOverrideHandler.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.behavior.IBehavior; import baritone.api.utils.input.Input; /** * @author Brady * @since 11/12/2018 */ public interface IInputOverrideHandler extends IBehavior { boolean isInputForcedDown(Input input); void setInputForceState(Input input, boolean forced); void clearAllKeys(); } ================================================ FILE: src/api/java/baritone/api/utils/IPlayerContext.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.cache.IWorldData; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import java.util.Optional; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * @author Brady * @since 11/12/2018 */ public interface IPlayerContext { Minecraft minecraft(); LocalPlayer player(); IPlayerController playerController(); Level world(); default Iterable<Entity> entities() { return ((ClientLevel) world()).entitiesForRendering(); } default Stream<Entity> entitiesStream() { return StreamSupport.stream(entities().spliterator(), false); } IWorldData worldData(); HitResult objectMouseOver(); default BetterBlockPos playerFeet() { // TODO find a better way to deal with soul sand!!!!! BetterBlockPos feet = new BetterBlockPos(player().position().x, player().position().y + 0.1251, player().position().z); // sometimes when calling this from another thread or while world is null, it'll throw a NullPointerException // that causes the game to immediately crash // // so of course crashing on 2b is horribly bad due to queue times and logout spot // catch the NPE and ignore it if it does happen // // this does not impact performance at all since we're not null checking constantly // if there is an exception, the only overhead is Java generating the exception object... so we can ignore it try { if (world().getBlockState(feet).getBlock() instanceof SlabBlock) { return feet.above(); } } catch (NullPointerException ignored) {} return feet; } default Vec3 playerFeetAsVec() { return new Vec3(player().position().x, player().position().y, player().position().z); } default Vec3 playerHead() { return new Vec3(player().position().x, player().position().y + player().getEyeHeight(), player().position().z); } default Vec3 playerMotion() { return player().getDeltaMovement(); } BetterBlockPos viewerPos(); default Rotation playerRotations() { return new Rotation(player().getYRot(), player().getXRot()); } /** * Returns the player's eye height, taking into account whether or not the player is sneaking. * * @param ifSneaking Whether or not the player is sneaking * @return The player's eye height * @deprecated Use entity.getEyeHeight(Pose.CROUCHING) instead */ @Deprecated static double eyeHeight(boolean ifSneaking) { return ifSneaking ? 1.27 : 1.62; } /** * Returns the block that the crosshair is currently placed over. Updated once per tick. * * @return The position of the highlighted block */ default Optional<BlockPos> getSelectedBlock() { HitResult result = objectMouseOver(); if (result != null && result.getType() == HitResult.Type.BLOCK) { return Optional.of(((BlockHitResult) result).getBlockPos()); } return Optional.empty(); } default boolean isLookingAt(BlockPos pos) { return getSelectedBlock().equals(Optional.of(pos)); } } ================================================ FILE: src/api/java/baritone/api/utils/IPlayerController.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.BaritoneAPI; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.ClickType; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.phys.BlockHitResult; /** * @author Brady * @since 12/14/2018 */ public interface IPlayerController { void syncHeldItem(); boolean hasBrokenBlock(); boolean onPlayerDamageBlock(BlockPos pos, Direction side); void resetBlockRemoving(); void windowClick(int windowId, int slotId, int mouseButton, ClickType type, Player player); GameType getGameType(); InteractionResult processRightClickBlock(LocalPlayer player, Level world, InteractionHand hand, BlockHitResult result); InteractionResult processRightClick(LocalPlayer player, Level world, InteractionHand hand); boolean clickBlock(BlockPos loc, Direction face); void setHittingBlock(boolean hittingBlock); default double getBlockReachDistance() { return this.getGameType().isCreative() ? 5.0F : BaritoneAPI.getSettings().blockReachDistance.value; } } ================================================ FILE: src/api/java/baritone/api/utils/MyChunkPos.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import com.google.gson.annotations.SerializedName; /** * Need a non obfed chunkpos that we can load using GSON */ public class MyChunkPos { @SerializedName("x") public int x; @SerializedName("z") public int z; @Override public String toString() { return x + ", " + z; } } ================================================ FILE: src/api/java/baritone/api/utils/NotificationHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import org.apache.commons.lang3.SystemUtils; import java.awt.*; import java.io.IOException; /** * This class is not called from the main game thread. * Do not refer to any Minecraft classes, it wouldn't be thread safe. * * @author aUniqueUser */ public class NotificationHelper { private static TrayIcon trayIcon; public static void notify(String text, boolean error) { if (SystemUtils.IS_OS_WINDOWS) { windows(text, error); } else if (SystemUtils.IS_OS_MAC_OSX) { mac(text); } else if (SystemUtils.IS_OS_LINUX) { linux(text); } } private static void windows(String text, boolean error) { if (SystemTray.isSupported()) { try { if (trayIcon == null) { SystemTray tray = SystemTray.getSystemTray(); Image image = Toolkit.getDefaultToolkit().createImage(""); trayIcon = new TrayIcon(image, "Baritone"); trayIcon.setImageAutoSize(true); trayIcon.setToolTip("Baritone"); tray.add(trayIcon); } trayIcon.displayMessage("Baritone", text, error ? TrayIcon.MessageType.ERROR : TrayIcon.MessageType.INFO); } catch (Exception e) { e.printStackTrace(); } } else { System.out.println("SystemTray is not supported"); } } private static void mac(String text) { ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.command("osascript", "-e", "display notification \"" + text + "\" with title \"Baritone\""); try { processBuilder.start(); } catch (IOException e) { e.printStackTrace(); } } // The only way to display notifications on linux is to use the java-gnome library, // or send notify-send to shell with a ProcessBuilder. Unfortunately the java-gnome // library is licenced under the GPL, see (https://en.wikipedia.org/wiki/Java-gnome) private static void linux(String text) { ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.command("notify-send", "-a", "Baritone", text); try { processBuilder.start(); } catch (IOException e) { e.printStackTrace(); } } } ================================================ FILE: src/api/java/baritone/api/utils/Pair.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import java.util.Objects; /** * @author Brady */ public final class Pair<A, B> { private final A a; private final B b; public Pair(A a, B b) { this.a = a; this.b = b; } public A first() { return this.a; } public B second() { return this.b; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || o.getClass() != Pair.class) { return false; } Pair<?, ?> pair = (Pair<?, ?>) o; return Objects.equals(this.a, pair.a) && Objects.equals(this.b, pair.b); } @Override public int hashCode() { return 31 * Objects.hashCode(this.a) + Objects.hashCode(this.b); } } ================================================ FILE: src/api/java/baritone/api/utils/PathCalculationResult.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.pathing.calc.IPath; import java.util.Objects; import java.util.Optional; public class PathCalculationResult { private final IPath path; private final Type type; public PathCalculationResult(Type type) { this(type, null); } public PathCalculationResult(Type type, IPath path) { Objects.requireNonNull(type); this.path = path; this.type = type; } public final Optional<IPath> getPath() { return Optional.ofNullable(this.path); } public final Type getType() { return this.type; } public enum Type { SUCCESS_TO_GOAL, SUCCESS_SEGMENT, FAILURE, CANCELLATION, EXCEPTION, } } ================================================ FILE: src/api/java/baritone/api/utils/RayTraceUtils.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Pose; import net.minecraft.world.level.ClipContext; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; /** * @author Brady * @since 8/25/2018 */ public final class RayTraceUtils { private RayTraceUtils() {} /** * Performs a block raytrace with the specified rotations. This should only be used when * any entity collisions can be ignored, because this method will not recognize if an * entity is in the way or not. The local player's block reach distance will be used. * * @param entity The entity representing the raytrace source * @param rotation The rotation to raytrace towards * @param blockReachDistance The block reach distance of the entity * @return The calculated raytrace result */ public static HitResult rayTraceTowards(Entity entity, Rotation rotation, double blockReachDistance) { return rayTraceTowards(entity, rotation, blockReachDistance, false); } public static HitResult rayTraceTowards(Entity entity, Rotation rotation, double blockReachDistance, boolean wouldSneak) { Vec3 start; if (wouldSneak) { start = inferSneakingEyePosition(entity); } else { start = entity.getEyePosition(1.0F); // do whatever is correct } Vec3 direction = RotationUtils.calcLookDirectionFromRotation(rotation); Vec3 end = start.add( direction.x * blockReachDistance, direction.y * blockReachDistance, direction.z * blockReachDistance ); return entity.level.clip(new ClipContext(start, end, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity)); } public static Vec3 inferSneakingEyePosition(Entity entity) { return new Vec3(entity.getX(), entity.getY() + entity.getEyeHeight(Pose.CROUCHING), entity.getZ()); } } ================================================ FILE: src/api/java/baritone/api/utils/Rotation.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; /** * @author Brady * @since 9/25/2018 */ public class Rotation { /** * The yaw angle of this Rotation */ private final float yaw; /** * The pitch angle of this Rotation */ private final float pitch; public Rotation(float yaw, float pitch) { this.yaw = yaw; this.pitch = pitch; if (Float.isInfinite(yaw) || Float.isNaN(yaw) || Float.isInfinite(pitch) || Float.isNaN(pitch)) { throw new IllegalStateException(yaw + " " + pitch); } } /** * @return The yaw of this rotation */ public float getYaw() { return this.yaw; } /** * @return The pitch of this rotation */ public float getPitch() { return this.pitch; } /** * Adds the yaw/pitch of the specified rotations to this * rotation's yaw/pitch, and returns the result. * * @param other Another rotation * @return The result from adding the other rotation to this rotation */ public Rotation add(Rotation other) { return new Rotation( this.yaw + other.yaw, this.pitch + other.pitch ); } /** * Subtracts the yaw/pitch of the specified rotations from this * rotation's yaw/pitch, and returns the result. * * @param other Another rotation * @return The result from subtracting the other rotation from this rotation */ public Rotation subtract(Rotation other) { return new Rotation( this.yaw - other.yaw, this.pitch - other.pitch ); } /** * @return A copy of this rotation with the pitch clamped */ public Rotation clamp() { return new Rotation( this.yaw, clampPitch(this.pitch) ); } /** * @return A copy of this rotation with the yaw normalized */ public Rotation normalize() { return new Rotation( normalizeYaw(this.yaw), this.pitch ); } /** * @return A copy of this rotation with the pitch clamped and the yaw normalized */ public Rotation normalizeAndClamp() { return new Rotation( normalizeYaw(this.yaw), clampPitch(this.pitch) ); } public Rotation withPitch(float pitch) { return new Rotation(this.yaw, pitch); } /** * Is really close to * * @param other another rotation * @return are they really close */ public boolean isReallyCloseTo(Rotation other) { return yawIsReallyClose(other) && Math.abs(this.pitch - other.pitch) < 0.01; } public boolean yawIsReallyClose(Rotation other) { float yawDiff = Math.abs(normalizeYaw(yaw) - normalizeYaw(other.yaw)); // you cant fool me return (yawDiff < 0.01 || yawDiff > 359.99); } /** * Clamps the specified pitch value between -90 and 90. * * @param pitch The input pitch * @return The clamped pitch */ public static float clampPitch(float pitch) { return Math.max(-90, Math.min(90, pitch)); } /** * Normalizes the specified yaw value between -180 and 180. * * @param yaw The input yaw * @return The normalized yaw */ public static float normalizeYaw(float yaw) { float newYaw = yaw % 360F; if (newYaw < -180F) { newYaw += 360F; } if (newYaw > 180F) { newYaw -= 360F; } return newYaw; } /** * Gets the distance between a starting yaw and an offset yaw. * Distance can be negative if the offset yaw is behind of the starting yaw. * * @param yaw The initial yaw * @param offsetYaw The offset yaw * @return The distance between the yaws */ public static float yawDistanceFromOffset(float yaw, float offsetYaw) { if ((yaw > 0 ^ offsetYaw > 0) && ((yaw > 90 || yaw < -90) ^ (offsetYaw > 90 || offsetYaw < -90))) { if (yaw < 0) { return 360 + (yaw - offsetYaw); } else { return 360 - (yaw - offsetYaw); } } else { return yaw - offsetYaw; } } @Override public String toString() { return "Yaw: " + yaw + ", Pitch: " + pitch; } } ================================================ FILE: src/api/java/baritone/api/utils/RotationUtils.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import java.util.Optional; /** * @author Brady * @since 9/25/2018 */ public final class RotationUtils { /** * Constant that a degree value is multiplied by to get the equivalent radian value */ public static final double DEG_TO_RAD = Math.PI / 180.0; public static final float DEG_TO_RAD_F = (float) DEG_TO_RAD; /** * Constant that a radian value is multiplied by to get the equivalent degree value */ public static final double RAD_TO_DEG = 180.0 / Math.PI; public static final float RAD_TO_DEG_F = (float) RAD_TO_DEG; /** * Offsets from the root block position to the center of each side. */ private static final Vec3[] BLOCK_SIDE_MULTIPLIERS = new Vec3[]{ new Vec3(0.5, 0, 0.5), // Down new Vec3(0.5, 1, 0.5), // Up new Vec3(0.5, 0.5, 0), // North new Vec3(0.5, 0.5, 1), // South new Vec3(0, 0.5, 0.5), // West new Vec3(1, 0.5, 0.5) // East }; private RotationUtils() {} /** * Calculates the rotation from BlockPos<sub>dest</sub> to BlockPos<sub>orig</sub> * * @param orig The origin position * @param dest The destination position * @return The rotation from the origin to the destination */ public static Rotation calcRotationFromCoords(BlockPos orig, BlockPos dest) { return calcRotationFromVec3d(new Vec3(orig.getX(), orig.getY(), orig.getZ()), new Vec3(dest.getX(), dest.getY(), dest.getZ())); } /** * Wraps the target angles to a relative value from the current angles. This is done by * subtracting the current from the target, normalizing it, and then adding the current * angles back to it. * * @param current The current angles * @param target The target angles * @return The wrapped angles */ public static Rotation wrapAnglesToRelative(Rotation current, Rotation target) { if (current.yawIsReallyClose(target)) { return new Rotation(current.getYaw(), target.getPitch()); } return target.subtract(current).normalize().add(current); } /** * Calculates the rotation from Vec<sub>dest</sub> to Vec<sub>orig</sub> and makes the * return value relative to the specified current rotations. * * @param orig The origin position * @param dest The destination position * @param current The current rotations * @return The rotation from the origin to the destination * @see #wrapAnglesToRelative(Rotation, Rotation) */ public static Rotation calcRotationFromVec3d(Vec3 orig, Vec3 dest, Rotation current) { return wrapAnglesToRelative(current, calcRotationFromVec3d(orig, dest)); } /** * Calculates the rotation from Vec<sub>dest</sub> to Vec<sub>orig</sub> * * @param orig The origin position * @param dest The destination position * @return The rotation from the origin to the destination */ private static Rotation calcRotationFromVec3d(Vec3 orig, Vec3 dest) { double[] delta = {orig.x - dest.x, orig.y - dest.y, orig.z - dest.z}; double yaw = Mth.atan2(delta[0], -delta[2]); double dist = Math.sqrt(delta[0] * delta[0] + delta[2] * delta[2]); double pitch = Mth.atan2(delta[1], dist); return new Rotation( (float) (yaw * RAD_TO_DEG), (float) (pitch * RAD_TO_DEG) ); } /** * Calculates the look vector for the specified yaw/pitch rotations. * * @param rotation The input rotation * @return Look vector for the rotation */ public static Vec3 calcLookDirectionFromRotation(Rotation rotation) { float flatZ = Mth.cos((-rotation.getYaw() * DEG_TO_RAD_F) - (float) Math.PI); float flatX = Mth.sin((-rotation.getYaw() * DEG_TO_RAD_F) - (float) Math.PI); float pitchBase = -Mth.cos(-rotation.getPitch() * DEG_TO_RAD_F); float pitchHeight = Mth.sin(-rotation.getPitch() * DEG_TO_RAD_F); return new Vec3(flatX * pitchBase, pitchHeight, flatZ * pitchBase); } @Deprecated public static Vec3 calcVec3dFromRotation(Rotation rotation) { return calcLookDirectionFromRotation(rotation); } /** * @param ctx Context for the viewing entity * @param pos The target block position * @return The optional rotation * @see #reachable(IPlayerContext, BlockPos, double) */ public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos) { return reachable(ctx, pos, false); } public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos, boolean wouldSneak) { return reachable(ctx, pos, ctx.playerController().getBlockReachDistance(), wouldSneak); } /** * Determines if the specified entity is able to reach the center of any of the sides * of the specified block. It first checks if the block center is reachable, and if so, * that rotation will be returned. If not, it will return the first center of a given * side that is reachable. The return type will be {@link Optional#empty()} if the entity is * unable to reach any of the sides of the block. * * @param ctx Context for the viewing entity * @param pos The target block position * @param blockReachDistance The block reach distance of the entity * @return The optional rotation */ public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos, double blockReachDistance) { return reachable(ctx, pos, blockReachDistance, false); } public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos, double blockReachDistance, boolean wouldSneak) { if (BaritoneAPI.getSettings().remainWithExistingLookDirection.value && ctx.isLookingAt(pos)) { /* * why add 0.0001? * to indicate that we actually have a desired pitch * the way we indicate that the pitch can be whatever and we only care about the yaw * is by setting the desired pitch to the current pitch * setting the desired pitch to the current pitch + 0.0001 means that we do have a desired pitch, it's * just what it currently is * * or if you're a normal person literally all this does it ensure that we don't nudge the pitch to a normal level */ Rotation hypothetical = ctx.playerRotations().add(new Rotation(0, 0.0001F)); if (wouldSneak) { // the concern here is: what if we're looking at it now, but as soon as we start sneaking we no longer are HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), hypothetical, blockReachDistance, true); if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(pos)) { return Optional.of(hypothetical); // yes, if we sneaked we would still be looking at the block } } else { return Optional.of(hypothetical); } } Optional<Rotation> possibleRotation = reachableCenter(ctx, pos, blockReachDistance, wouldSneak); //System.out.println("center: " + possibleRotation); if (possibleRotation.isPresent()) { return possibleRotation; } BlockState state = ctx.world().getBlockState(pos); VoxelShape shape = state.getShape(ctx.world(), pos); if (shape.isEmpty()) { shape = Shapes.block(); } for (Vec3 sideOffset : BLOCK_SIDE_MULTIPLIERS) { double xDiff = shape.min(Direction.Axis.X) * sideOffset.x + shape.max(Direction.Axis.X) * (1 - sideOffset.x); double yDiff = shape.min(Direction.Axis.Y) * sideOffset.y + shape.max(Direction.Axis.Y) * (1 - sideOffset.y); double zDiff = shape.min(Direction.Axis.Z) * sideOffset.z + shape.max(Direction.Axis.Z) * (1 - sideOffset.z); possibleRotation = reachableOffset(ctx, pos, new Vec3(pos.getX(), pos.getY(), pos.getZ()).add(xDiff, yDiff, zDiff), blockReachDistance, wouldSneak); if (possibleRotation.isPresent()) { return possibleRotation; } } return Optional.empty(); } /** * Determines if the specified entity is able to reach the specified block with * the given offsetted position. The return type will be {@link Optional#empty()} if * the entity is unable to reach the block with the offset applied. * * @param ctx Context for the viewing entity * @param pos The target block position * @param offsetPos The position of the block with the offset applied. * @param blockReachDistance The block reach distance of the entity * @return The optional rotation */ public static Optional<Rotation> reachableOffset(IPlayerContext ctx, BlockPos pos, Vec3 offsetPos, double blockReachDistance, boolean wouldSneak) { Vec3 eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(ctx.player()) : ctx.player().getEyePosition(1.0F); Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, ctx.playerRotations()); Rotation actualRotation = BaritoneAPI.getProvider().getBaritoneForPlayer(ctx.player()).getLookBehavior().getAimProcessor().peekRotation(rotation); HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), actualRotation, blockReachDistance, wouldSneak); //System.out.println(result); if (result != null && result.getType() == HitResult.Type.BLOCK) { if (((BlockHitResult) result).getBlockPos().equals(pos)) { return Optional.of(rotation); } if (ctx.world().getBlockState(pos).getBlock() instanceof BaseFireBlock && ((BlockHitResult) result).getBlockPos().equals(pos.below())) { return Optional.of(rotation); } } return Optional.empty(); } /** * Determines if the specified entity is able to reach the specified block where it is * looking at the direct center of it's hitbox. * * @param ctx Context for the viewing entity * @param pos The target block position * @param blockReachDistance The block reach distance of the entity * @return The optional rotation */ public static Optional<Rotation> reachableCenter(IPlayerContext ctx, BlockPos pos, double blockReachDistance, boolean wouldSneak) { return reachableOffset(ctx, pos, VecUtils.calculateBlockCenter(ctx.world(), pos), blockReachDistance, wouldSneak); } @Deprecated public static Optional<Rotation> reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance) { return reachable(entity, pos, blockReachDistance, false); } @Deprecated public static Optional<Rotation> reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(entity); IPlayerContext ctx = baritone.getPlayerContext(); return reachable(ctx, pos, blockReachDistance, wouldSneak); } @Deprecated public static Optional<Rotation> reachableOffset(Entity entity, BlockPos pos, Vec3 offsetPos, double blockReachDistance, boolean wouldSneak) { Vec3 eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(entity) : entity.getEyePosition(1.0F); Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, new Rotation(entity.getYRot(), entity.getXRot())); HitResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance, wouldSneak); //System.out.println(result); if (result != null && result.getType() == HitResult.Type.BLOCK) { if (((BlockHitResult) result).getBlockPos().equals(pos)) { return Optional.of(rotation); } if (entity.level.getBlockState(pos).getBlock() instanceof BaseFireBlock && ((BlockHitResult) result).getBlockPos().equals(pos.below())) { return Optional.of(rotation); } } return Optional.empty(); } @Deprecated public static Optional<Rotation> reachableCenter(Entity entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { return reachableOffset(entity, pos, VecUtils.calculateBlockCenter(entity.level, pos), blockReachDistance, wouldSneak); } } ================================================ FILE: src/api/java/baritone/api/utils/SettingsUtil.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import baritone.api.BaritoneAPI; import baritone.api.Settings; import net.minecraft.client.Minecraft; import net.minecraft.core.Direction; import net.minecraft.core.Registry; import net.minecraft.core.Vec3i; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import java.awt.*; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; public class SettingsUtil { public static final String SETTINGS_DEFAULT_NAME = "settings.txt"; private static final Pattern SETTING_PATTERN = Pattern.compile("^(?<setting>[^ ]+) +(?<value>.+)"); // key and value split by the first space private static boolean isComment(String line) { return line.startsWith("#") || line.startsWith("//"); } private static void forEachLine(Path file, Consumer<String> consumer) throws IOException { try (BufferedReader scan = Files.newBufferedReader(file)) { String line; while ((line = scan.readLine()) != null) { if (line.isEmpty() || isComment(line)) { continue; } consumer.accept(line); } } } public static void readAndApply(Settings settings, String settingsName) { try { forEachLine(settingsByName(settingsName), line -> { Matcher matcher = SETTING_PATTERN.matcher(line); if (!matcher.matches()) { Helper.HELPER.logDirect("Invalid syntax in setting file: " + line); return; } String settingName = matcher.group("setting").toLowerCase(); String settingValue = matcher.group("value"); // TODO remove soonish if ("allowjumpat256".equals(settingName)) { settingName = "allowjumpatbuildlimit"; } try { parseAndApply(settings, settingName, settingValue); } catch (Exception ex) { Helper.HELPER.logDirect("Unable to parse line " + line); ex.printStackTrace(); } }); } catch (NoSuchFileException ignored) { Helper.HELPER.logDirect("Baritone settings file not found, resetting."); } catch (Exception ex) { Helper.HELPER.logDirect("Exception while reading Baritone settings, some settings may be reset to default values!"); ex.printStackTrace(); } } public static synchronized void save(Settings settings) { try (BufferedWriter out = Files.newBufferedWriter(settingsByName(SETTINGS_DEFAULT_NAME))) { for (Settings.Setting setting : modifiedSettings(settings)) { out.write(settingToString(setting) + "\n"); } } catch (Exception ex) { Helper.HELPER.logDirect("Exception thrown while saving Baritone settings!"); ex.printStackTrace(); } } private static Path settingsByName(String name) { return Minecraft.getInstance().gameDirectory.toPath().resolve("baritone").resolve(name); } public static List<Settings.Setting> modifiedSettings(Settings settings) { List<Settings.Setting> modified = new ArrayList<>(); for (Settings.Setting setting : settings.allSettings) { if (setting.value == null) { System.out.println("NULL SETTING?" + setting.getName()); continue; } if (setting.isJavaOnly()) { continue; // NO } if (setting.value == setting.defaultValue) { continue; } modified.add(setting); } return modified; } /** * Gets the type of a setting and returns it as a string, with package names stripped. * <p> * For example, if the setting type is {@code java.util.List<java.lang.String>}, this function returns * {@code List<String>}. * * @param setting The setting * @return The type */ public static String settingTypeToString(Settings.Setting setting) { return setting.getType().getTypeName() .replaceAll("(?:\\w+\\.)+(\\w+)", "$1"); } public static <T> String settingValueToString(Settings.Setting<T> setting, T value) throws IllegalArgumentException { Parser io = Parser.getParser(setting.getType()); if (io == null) { throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting.getName()); } return io.toString(setting.getType(), value); } public static String settingValueToString(Settings.Setting setting) throws IllegalArgumentException { //noinspection unchecked return settingValueToString(setting, setting.value); } public static String settingDefaultToString(Settings.Setting setting) throws IllegalArgumentException { //noinspection unchecked return settingValueToString(setting, setting.defaultValue); } public static String maybeCensor(int coord) { if (BaritoneAPI.getSettings().censorCoordinates.value) { return "<censored>"; } return Integer.toString(coord); } public static String settingToString(Settings.Setting setting) throws IllegalStateException { if (setting.isJavaOnly()) { return setting.getName(); } return setting.getName() + " " + settingValueToString(setting); } /** * Deprecated. Use {@link Settings.Setting#isJavaOnly()} instead. * * @param setting The Setting * @return true if the setting can not be set or read by the user */ @Deprecated public static boolean javaOnlySetting(Settings.Setting setting) { return setting.isJavaOnly(); } public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { Settings.Setting setting = settings.byLowerName.get(settingName); if (setting == null) { throw new IllegalStateException("No setting by that name"); } Class intendedType = setting.getValueClass(); ISettingParser ioMethod = Parser.getParser(setting.getType()); Object parsed = ioMethod.parse(setting.getType(), settingValue); if (!intendedType.isInstance(parsed)) { throw new IllegalStateException(ioMethod + " parser returned incorrect type, expected " + intendedType + " got " + parsed + " which is " + parsed.getClass()); } setting.value = parsed; } private interface ISettingParser<T> { T parse(Type type, String raw); String toString(Type type, T value); boolean accepts(Type type); } private enum Parser implements ISettingParser { DOUBLE(Double.class, Double::parseDouble), BOOLEAN(Boolean.class, Boolean::parseBoolean), INTEGER(Integer.class, Integer::parseInt), FLOAT(Float.class, Float::parseFloat), LONG(Long.class, Long::parseLong), STRING(String.class, String::new), MIRROR(Mirror.class, Mirror::valueOf, Mirror::name), ROTATION(Rotation.class, Rotation::valueOf, Rotation::name), COLOR( Color.class, str -> new Color(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), color -> color.getRed() + "," + color.getGreen() + "," + color.getBlue() ), VEC3I( Vec3i.class, str -> new Vec3i(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), vec -> vec.getX() + "," + vec.getY() + "," + vec.getZ() ), BLOCK( Block.class, str -> BlockUtils.stringToBlockRequired(str.trim()), BlockUtils::blockToString ), ITEM( Item.class, str -> BuiltInRegistries.ITEM.get(new ResourceLocation(str.trim())), // TODO this now returns AIR on failure instead of null, is that an issue? item -> BuiltInRegistries.ITEM.getKey(item).toString() ), LIST() { @Override public Object parse(Type type, String raw) { Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; Parser parser = Parser.getParser(elementType); return Stream.of(raw.split(",")) .map(s -> parser.parse(elementType, s)) .collect(Collectors.toList()); } @Override public String toString(Type type, Object value) { Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; Parser parser = Parser.getParser(elementType); return ((List<?>) value).stream() .map(o -> parser.toString(elementType, o)) .collect(Collectors.joining(",")); } @Override public boolean accepts(Type type) { return List.class.isAssignableFrom(TypeUtils.resolveBaseClass(type)); } }, MAPPING() { @Override public Object parse(Type type, String raw) { Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0]; Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1]; Parser keyParser = Parser.getParser(keyType); Parser valueParser = Parser.getParser(valueType); return Stream.of(raw.split(",(?=[^,]*->)")) .map(s -> s.split("->")) .collect(Collectors.toMap(s -> keyParser.parse(keyType, s[0]), s -> valueParser.parse(valueType, s[1]))); } @Override public String toString(Type type, Object value) { Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0]; Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1]; Parser keyParser = Parser.getParser(keyType); Parser valueParser = Parser.getParser(valueType); return ((Map<?, ?>) value).entrySet().stream() .map(o -> keyParser.toString(keyType, o.getKey()) + "->" + valueParser.toString(valueType, o.getValue())) .collect(Collectors.joining(",")); } @Override public boolean accepts(Type type) { return Map.class.isAssignableFrom(TypeUtils.resolveBaseClass(type)); } }; private final Class<?> cla$$; private final Function<String, Object> parser; private final Function<Object, String> toString; Parser() { this.cla$$ = null; this.parser = null; this.toString = null; } <T> Parser(Class<T> cla$$, Function<String, T> parser) { this(cla$$, parser, Object::toString); } <T> Parser(Class<T> cla$$, Function<String, T> parser, Function<T, String> toString) { this.cla$$ = cla$$; this.parser = parser::apply; this.toString = x -> toString.apply((T) x); } @Override public Object parse(Type type, String raw) { Object parsed = this.parser.apply(raw); Objects.requireNonNull(parsed); return parsed; } @Override public String toString(Type type, Object value) { return this.toString.apply(value); } @Override public boolean accepts(Type type) { return type instanceof Class && this.cla$$.isAssignableFrom((Class) type); } public static Parser getParser(Type type) { return Stream.of(values()) .filter(parser -> parser.accepts(type)) .findFirst().orElse(null); } } } ================================================ FILE: src/api/java/baritone/api/utils/TypeUtils.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * @author Brady * @since 4/20/2019 */ public final class TypeUtils { private TypeUtils() {} /** * Resolves the "base type" for the specified type. For example, if the specified * type is {@code List<String>}, then {@code List.class} will be returned. If the * specified type is already a class, then it is directly returned. * * @param type The type to resolve * @return The base class */ public static Class<?> resolveBaseClass(Type type) { return type instanceof Class ? (Class<?>) type : type instanceof ParameterizedType ? (Class<?>) ((ParameterizedType) type).getRawType() : null; } } ================================================ FILE: src/api/java/baritone/api/utils/VecUtils.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; /** * @author Brady * @since 10/13/2018 */ public final class VecUtils { private VecUtils() {} /** * Calculates the center of the block at the specified position's bounding box * * @param world The world that the block is in, used to provide the bounding box * @param pos The block position * @return The center of the block's bounding box * @see #getBlockPosCenter(BlockPos) */ public static Vec3 calculateBlockCenter(Level world, BlockPos pos) { BlockState b = world.getBlockState(pos); VoxelShape shape = b.getCollisionShape(world, pos); if (shape.isEmpty()) { return getBlockPosCenter(pos); } double xDiff = (shape.min(Direction.Axis.X) + shape.max(Direction.Axis.X)) / 2; double yDiff = (shape.min(Direction.Axis.Y) + shape.max(Direction.Axis.Y)) / 2; double zDiff = (shape.min(Direction.Axis.Z) + shape.max(Direction.Axis.Z)) / 2; if (Double.isNaN(xDiff) || Double.isNaN(yDiff) || Double.isNaN(zDiff)) { throw new IllegalStateException(b + " " + pos + " " + shape); } if (b.getBlock() instanceof BaseFireBlock) {//look at bottom of fire when putting it out yDiff = 0; } return new Vec3( pos.getX() + xDiff, pos.getY() + yDiff, pos.getZ() + zDiff ); } /** * Gets the assumed center position of the given block position. * This is done by adding 0.5 to the X, Y, and Z axes. * <p> * TODO: We may want to consider replacing many usages of this method with #calculateBlockCenter(BlockPos) * * @param pos The block position * @return The assumed center of the position * @see #calculateBlockCenter(Level, BlockPos) */ public static Vec3 getBlockPosCenter(BlockPos pos) { return new Vec3(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); } /** * Gets the distance from the specified position to the assumed center of the specified block position. * * @param pos The block position * @param x The x pos * @param y The y pos * @param z The z pos * @return The distance from the assumed block center to the position * @see #getBlockPosCenter(BlockPos) */ public static double distanceToCenter(BlockPos pos, double x, double y, double z) { double xdiff = pos.getX() + 0.5 - x; double ydiff = pos.getY() + 0.5 - y; double zdiff = pos.getZ() + 0.5 - z; return Math.sqrt(xdiff * xdiff + ydiff * ydiff + zdiff * zdiff); } /** * Gets the distance from the specified entity's position to the assumed * center of the specified block position. * * @param entity The entity * @param pos The block position * @return The distance from the entity to the block's assumed center * @see #getBlockPosCenter(BlockPos) */ public static double entityDistanceToCenter(Entity entity, BlockPos pos) { return distanceToCenter(pos, entity.position().x, entity.position().y, entity.position().z); } /** * Gets the distance from the specified entity's position to the assumed * center of the specified block position, ignoring the Y axis. * * @param entity The entity * @param pos The block position * @return The horizontal distance from the entity to the block's assumed center * @see #getBlockPosCenter(BlockPos) */ public static double entityFlatDistanceToCenter(Entity entity, BlockPos pos) { return distanceToCenter(pos, entity.position().x, pos.getY() + 0.5, entity.position().z); } } ================================================ FILE: src/api/java/baritone/api/utils/accessor/IItemStack.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils.accessor; public interface IItemStack { int getBaritoneHash(); } ================================================ FILE: src/api/java/baritone/api/utils/gui/BaritoneToast.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils.gui; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.toasts.Toast; import net.minecraft.client.gui.components.toasts.ToastComponent; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; public class BaritoneToast implements Toast { private String title; private String subtitle; private long firstDrawTime; private boolean newDisplay; private long totalShowTime; public BaritoneToast(Component titleComponent, Component subtitleComponent, long totalShowTime) { this.title = titleComponent.getString(); this.subtitle = subtitleComponent == null ? null : subtitleComponent.getString(); this.totalShowTime = totalShowTime; } public Visibility render(PoseStack matrixStack, ToastComponent toastGui, long delta) { if (this.newDisplay) { this.firstDrawTime = delta; this.newDisplay = false; } //TODO: check toastGui.getMinecraft().getTextureManager().bindForSetup(new ResourceLocation("textures/gui/toasts.png")); //GlStateManager._color4f(1.0F, 1.0F, 1.0F, 255.0F); toastGui.blit(matrixStack, 0, 0, 0, 32, 160, 32); if (this.subtitle == null) { toastGui.getMinecraft().font.draw(matrixStack, this.title, 18, 12, -11534256); } else { toastGui.getMinecraft().font.draw(matrixStack, this.title, 18, 7, -11534256); toastGui.getMinecraft().font.draw(matrixStack, this.subtitle, 18, 18, -16777216); } return delta - this.firstDrawTime < totalShowTime ? Visibility.SHOW : Visibility.HIDE; } public void setDisplayedText(Component titleComponent, Component subtitleComponent) { this.title = titleComponent.getString(); this.subtitle = subtitleComponent == null ? null : subtitleComponent.getString(); this.newDisplay = true; } public static void addOrUpdate(ToastComponent toast, Component title, Component subtitle, long totalShowTime) { BaritoneToast baritonetoast = toast.getToast(BaritoneToast.class, new Object()); if (baritonetoast == null) { toast.addToast(new BaritoneToast(title, subtitle, totalShowTime)); } else { baritonetoast.setDisplayedText(title, subtitle); } } public static void addOrUpdate(Component title, Component subtitle) { addOrUpdate(Minecraft.getInstance().getToasts(), title, subtitle, baritone.api.BaritoneAPI.getSettings().toastTimer.value); } } ================================================ FILE: src/api/java/baritone/api/utils/input/Input.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils.input; /** * An {@link Enum} representing the inputs that control the player's * behavior. This includes moving, interacting with blocks, jumping, * sneaking, and sprinting. */ public enum Input { /** * The move forward input */ MOVE_FORWARD, /** * The move back input */ MOVE_BACK, /** * The move left input */ MOVE_LEFT, /** * The move right input */ MOVE_RIGHT, /** * The attack input */ CLICK_LEFT, /** * The use item input */ CLICK_RIGHT, /** * The jump input */ JUMP, /** * The sneak input */ SNEAK, /** * The sprint input */ SPRINT } ================================================ FILE: src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.api.utils.interfaces; import net.minecraft.core.BlockPos; public interface IGoalRenderPos { BlockPos getGoalPos(); } ================================================ FILE: src/launch/java/baritone/launch/BaritoneMixinConnector.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch; import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.asm.mixin.connect.IMixinConnector; public class BaritoneMixinConnector implements IMixinConnector { @Override public void connect() { Mixins.addConfiguration("mixins.baritone.json"); } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinChunkArray.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.utils.accessor.IChunkArray; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import java.util.concurrent.atomic.AtomicReferenceArray; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.LevelChunk; @Mixin(targets = "net.minecraft.client.multiplayer.ClientChunkCache$Storage") public abstract class MixinChunkArray implements IChunkArray { @Final @Shadow AtomicReferenceArray<LevelChunk> chunks; @Final @Shadow int chunkRadius; @Final @Shadow private int viewRange; @Shadow int viewCenterX; @Shadow int viewCenterZ; @Shadow int chunkCount; @Shadow abstract boolean inRange(int x, int z); @Shadow abstract int getIndex(int x, int z); @Shadow protected abstract void replace(int index, LevelChunk chunk); @Override public int centerX() { return viewCenterX; } @Override public int centerZ() { return viewCenterZ; } @Override public int viewDistance() { return chunkRadius; } @Override public AtomicReferenceArray<LevelChunk> getChunks() { return chunks; } @Override public void copyFrom(IChunkArray other) { viewCenterX = other.centerX(); viewCenterZ = other.centerZ(); AtomicReferenceArray<LevelChunk> copyingFrom = other.getChunks(); for (int k = 0; k < copyingFrom.length(); ++k) { LevelChunk chunk = copyingFrom.get(k); if (chunk != null) { ChunkPos chunkpos = chunk.getPos(); if (inRange(chunkpos.x, chunkpos.z)) { int index = getIndex(chunkpos.x, chunkpos.z); if (chunks.get(index) != null) { throw new IllegalStateException("Doing this would mutate the client's REAL loaded chunks?!"); } replace(index, chunk); } } } } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinClientChunkProvider.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.utils.accessor.IChunkArray; import baritone.utils.accessor.IClientChunkProvider; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import java.lang.reflect.Field; import java.util.Arrays; import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.client.multiplayer.ClientLevel; @Mixin(ClientChunkCache.class) public class MixinClientChunkProvider implements IClientChunkProvider { @Final @Shadow ClientLevel level; @Override public ClientChunkCache createThreadSafeCopy() { IChunkArray arr = extractReferenceArray(); ClientChunkCache result = new ClientChunkCache(level, arr.viewDistance() - 3); // -3 because its adds 3 for no reason lmao IChunkArray copyArr = ((IClientChunkProvider) result).extractReferenceArray(); copyArr.copyFrom(arr); if (copyArr.viewDistance() != arr.viewDistance()) { throw new IllegalStateException(copyArr.viewDistance() + " " + arr.viewDistance()); } return result; } @Override public IChunkArray extractReferenceArray() { for (Field f : ClientChunkCache.class.getDeclaredFields()) { if (IChunkArray.class.isAssignableFrom(f.getType())) { try { return (IChunkArray) f.get(this); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } throw new RuntimeException(Arrays.toString(ClientChunkCache.class.getDeclaredFields())); } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.BlockChangeEvent; import baritone.api.event.events.ChatEvent; import baritone.api.event.events.ChunkEvent; import baritone.api.event.events.type.EventState; import baritone.api.utils.Pair; import baritone.cache.CachedChunk; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.*; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.ArrayList; import java.util.List; /** * @author Brady * @since 8/3/2018 */ @Mixin(ClientPacketListener.class) public class MixinClientPlayNetHandler { // unused lol /*@Inject( method = "handleChunkData", at = @At( value = "INVOKE", target = "net/minecraft/client/multiplayer/ChunkProviderClient.func_212474_a(IILnet/minecraft/network/PacketBuffer;IZ)Lnet/minecraft/world/chunk/Chunk;" ) ) private void preRead(SPacketChunkData packetIn, CallbackInfo ci) { for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { ClientPlayerEntity player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPlayNetHandler) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( new ChunkEvent( EventState.PRE, packetIn.isFullChunk() ? ChunkEvent.Type.POPULATE_FULL : ChunkEvent.Type.POPULATE_PARTIAL, packetIn.getChunkX(), packetIn.getChunkZ() ) ); } } }*/ @Shadow @Final private Minecraft minecraft; @Inject( method = "sendChat(Ljava/lang/String;)V", at = @At("HEAD"), cancellable = true ) private void sendChatMessage(String string, CallbackInfo ci) { ChatEvent event = new ChatEvent(string); IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(this.minecraft.player); if (baritone == null) { return; } baritone.getGameEventHandler().onSendChatMessage(event); if (event.isCancelled()) { ci.cancel(); } } @Inject( method = "handleLevelChunkWithLight", at = @At("RETURN") ) private void postHandleChunkData(ClientboundLevelChunkWithLightPacket packetIn, CallbackInfo ci) { for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { LocalPlayer player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPacketListener) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( new ChunkEvent( EventState.POST, !packetIn.isSkippable() ? ChunkEvent.Type.POPULATE_FULL : ChunkEvent.Type.POPULATE_PARTIAL, packetIn.getX(), packetIn.getZ() ) ); } } } @Inject( method = "handleForgetLevelChunk", at = @At("HEAD") ) private void preChunkUnload(ClientboundForgetLevelChunkPacket packet, CallbackInfo ci) { for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { LocalPlayer player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPacketListener) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( new ChunkEvent(EventState.PRE, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ()) ); } } } @Inject( method = "handleForgetLevelChunk", at = @At("RETURN") ) private void postChunkUnload(ClientboundForgetLevelChunkPacket packet, CallbackInfo ci) { for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { LocalPlayer player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPacketListener) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( new ChunkEvent(EventState.POST, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ()) ); } } } @Inject( method = "handleBlockUpdate", at = @At("RETURN") ) private void postHandleBlockChange(ClientboundBlockUpdatePacket packetIn, CallbackInfo ci) { if (!Baritone.settings().repackOnAnyBlockChange.value) { return; } if (!CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(packetIn.getBlockState().getBlock())) { return; } for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { LocalPlayer player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPacketListener) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( new ChunkEvent( EventState.POST, ChunkEvent.Type.POPULATE_FULL, packetIn.getPos().getX() >> 4, packetIn.getPos().getZ() >> 4 ) ); } } } @Inject( method = "handleChunkBlocksUpdate", at = @At("RETURN") ) private void postHandleMultiBlockChange(ClientboundSectionBlocksUpdatePacket packetIn, CallbackInfo ci) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((ClientPacketListener) (Object) this); if (baritone == null) { return; } List<Pair<BlockPos, BlockState>> changes = new ArrayList<>(); packetIn.runUpdates((mutPos, state) -> { changes.add(new Pair<>(mutPos.immutable(), state)); }); if (changes.isEmpty()) { return; } baritone.getGameEventHandler().onBlockChange(new BlockChangeEvent( new ChunkPos(changes.get(0).first()), changes )); } @Inject( method = "handlePlayerCombatKill", at = @At( value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;shouldShowDeathScreen()Z" ) ) private void onPlayerDeath(ClientboundPlayerCombatKillPacket packetIn, CallbackInfo ci) { for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { LocalPlayer player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPacketListener) (Object) this) { ibaritone.getGameEventHandler().onPlayerDeath(); } } } /* @Inject( method = "handleChunkData", at = @At( value = "INVOKE", target = "net/minecraft/world/chunk/Chunk.read(Lnet/minecraft/network/PacketBuffer;IZ)V" ) ) private void preRead(SPacketChunkData packetIn, CallbackInfo ci) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((NetHandlerPlayClient) (Object) this); if (baritone == null) { return; } baritone.getGameEventHandler().onChunkEvent( new ChunkEvent( EventState.PRE, packetIn.isFullChunk() ? ChunkEvent.Type.POPULATE_FULL : ChunkEvent.Type.POPULATE_PARTIAL, packetIn.getChunkX(), packetIn.getChunkZ() ) ); } @Inject( method = "handleChunkData", at = @At("RETURN") ) private void postHandleChunkData(SPacketChunkData packetIn, CallbackInfo ci) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((NetHandlerPlayClient) (Object) this); if (baritone == null) { return; } baritone.getGameEventHandler().onChunkEvent( new ChunkEvent( EventState.POST, packetIn.isFullChunk() ? ChunkEvent.Type.POPULATE_FULL : ChunkEvent.Type.POPULATE_PARTIAL, packetIn.getChunkX(), packetIn.getChunkZ() ) ); } @Inject( method = "handleBlockChange", at = @At("RETURN") ) private void postHandleBlockChange(SPacketBlockChange packetIn, CallbackInfo ci) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((NetHandlerPlayClient) (Object) this); if (baritone == null) { return; } final ChunkPos pos = new ChunkPos(packetIn.getBlockPosition().getX() >> 4, packetIn.getBlockPosition().getZ() >> 4); final Pair<BlockPos, IBlockState> changed = new Pair<>(packetIn.getBlockPosition(), packetIn.getBlockState()); baritone.getGameEventHandler().onBlockChange(new BlockChangeEvent(pos, Collections.singletonList(changed))); } @Inject( method = "handleMultiBlockChange", at = @At("RETURN") ) private void postHandleMultiBlockChange(SPacketMultiBlockChange packetIn, CallbackInfo ci) { } */ } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.SprintStateEvent; import baritone.api.event.events.type.EventState; import baritone.behavior.LookBehavior; import net.minecraft.client.KeyMapping; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.player.Abilities; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** * @author Brady * @since 8/1/2018 */ @Mixin(LocalPlayer.class) public class MixinClientPlayerEntity { @Inject( method = "tick", at = @At( value = "INVOKE", target = "net/minecraft/client/player/AbstractClientPlayer.tick()V", shift = At.Shift.AFTER ) ) private void onPreUpdate(CallbackInfo ci) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); if (baritone != null) { baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.PRE)); } } @Redirect( method = "aiStep", at = @At( value = "FIELD", target = "net/minecraft/world/entity/player/Abilities.mayfly:Z" ) ) private boolean isAllowFlying(Abilities capabilities) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); if (baritone == null) { return capabilities.mayfly; } return !baritone.getPathingBehavior().isPathing() && capabilities.mayfly; } @Redirect( method = "aiStep", at = @At( value = "INVOKE", target = "net/minecraft/client/KeyMapping.isDown()Z" ) ) private boolean isKeyDown(KeyMapping keyBinding) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); if (baritone == null) { return keyBinding.isDown(); } SprintStateEvent event = new SprintStateEvent(); baritone.getGameEventHandler().onPlayerSprintState(event); if (event.getState() != null) { return event.getState(); } if (baritone != BaritoneAPI.getProvider().getPrimaryBaritone()) { // hitting control shouldn't make all bots sprint return false; } return keyBinding.isDown(); } @Inject( method = "rideTick", at = @At( value = "HEAD" ) ) private void updateRidden(CallbackInfo cb) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); if (baritone != null) { ((LookBehavior) baritone.getLookBehavior()).pig(); } } @Redirect( method = "aiStep", at = @At( value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;tryToStartFallFlying()Z" ) ) private boolean tryToStartFallFlying(final LocalPlayer instance) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(instance); if (baritone != null && baritone.getPathingBehavior().isPathing()) { return false; } return instance.tryToStartFallFlying(); } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinCommandSuggestionHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.event.events.TabCompleteEvent; import com.mojang.brigadier.ParseResults; import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.suggestion.Suggestion; import com.mojang.brigadier.suggestion.Suggestions; import net.minecraft.client.gui.components.CommandSuggestions; import net.minecraft.client.gui.components.EditBox; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @author Brady * @since 10/9/2019 */ @Mixin(CommandSuggestions.class) public class MixinCommandSuggestionHelper { @Shadow @Final EditBox input; @Shadow @Final private List<String> commandUsage; @Shadow private ParseResults currentParse; @Shadow private CompletableFuture<Suggestions> pendingSuggestions; @Shadow private CommandSuggestions.SuggestionsList suggestions; @Shadow boolean keepSuggestions; @Inject( method = "updateCommandInfo", at = @At("HEAD"), cancellable = true ) private void preUpdateSuggestion(CallbackInfo ci) { // Anything that is present in the input text before the cursor position String prefix = this.input.getValue().substring(0, Math.min(this.input.getValue().length(), this.input.getCursorPosition())); TabCompleteEvent event = new TabCompleteEvent(prefix); BaritoneAPI.getProvider().getPrimaryBaritone().getGameEventHandler().onPreTabComplete(event); if (event.isCancelled()) { ci.cancel(); return; } if (event.completions != null) { ci.cancel(); this.currentParse = null; // stop coloring if (this.keepSuggestions) { // Supress suggestions update when cycling suggestions. return; } this.input.setSuggestion(null); // clear old suggestions this.suggestions = null; // TODO: Support populating the command usage this.commandUsage.clear(); if (event.completions.length == 0) { this.pendingSuggestions = Suggestions.empty(); } else { StringRange range = StringRange.between(prefix.lastIndexOf(" ") + 1, prefix.length()); // if there is no space this starts at 0 List<Suggestion> suggestionList = Stream.of(event.completions) .map(s -> new Suggestion(range, s)) .collect(Collectors.toList()); Suggestions suggestions = new Suggestions(range, suggestionList); this.pendingSuggestions = new CompletableFuture<>(); this.pendingSuggestions.complete(suggestions); } ((CommandSuggestions) (Object) this).showSuggestions(true); // actually populate the suggestions list from the suggestions future } } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinEntity.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.event.events.RotationMoveEvent; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(Entity.class) public class MixinEntity { @Shadow private float yRot; @Shadow private float xRot; @Unique private RotationMoveEvent motionUpdateRotationEvent; @Inject( method = "moveRelative", at = @At("HEAD") ) private void moveRelativeHead(CallbackInfo info) { // noinspection ConstantConditions if (!LocalPlayer.class.isInstance(this) || BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this) == null) { return; } this.motionUpdateRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.MOTION_UPDATE, this.yRot, this.xRot); BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this).getGameEventHandler().onPlayerRotationMove(motionUpdateRotationEvent); this.yRot = this.motionUpdateRotationEvent.getYaw(); this.xRot = this.motionUpdateRotationEvent.getPitch(); } @Inject( method = "moveRelative", at = @At("RETURN") ) private void moveRelativeReturn(CallbackInfo info) { if (this.motionUpdateRotationEvent != null) { this.yRot = this.motionUpdateRotationEvent.getOriginal().getYaw(); this.xRot = this.motionUpdateRotationEvent.getOriginal().getPitch(); this.motionUpdateRotationEvent = null; } } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinEntityRenderManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.utils.accessor.IEntityRenderManager; import net.minecraft.client.renderer.entity.EntityRenderDispatcher; import org.spongepowered.asm.mixin.Mixin; @Mixin(EntityRenderDispatcher.class) public class MixinEntityRenderManager implements IEntityRenderManager { @Override public double renderPosX() { return ((EntityRenderDispatcher) (Object) this).camera.getPosition().x; } @Override public double renderPosY() { return ((EntityRenderDispatcher) (Object) this).camera.getPosition().y; } @Override public double renderPosZ() { return ((EntityRenderDispatcher) (Object) this).camera.getPosition().z; } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinFireworkRocketEntity.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.utils.accessor.IFireworkRocketEntity; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.level.Level; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import java.util.OptionalInt; @Mixin(FireworkRocketEntity.class) public abstract class MixinFireworkRocketEntity extends Entity implements IFireworkRocketEntity { @Shadow @Final private static EntityDataAccessor<OptionalInt> DATA_ATTACHED_TO_TARGET; @Shadow private LivingEntity attachedToEntity; @Shadow public abstract boolean isAttachedToEntity(); private MixinFireworkRocketEntity(Level level) { super(EntityType.FIREWORK_ROCKET, level); } @Override public LivingEntity getBoostedEntity() { if (this.isAttachedToEntity() && this.attachedToEntity == null) { // isAttachedToEntity checks if the optional is present final Entity entity = this.level.getEntity(this.entityData.get(DATA_ATTACHED_TO_TARGET).getAsInt()); if (entity instanceof LivingEntity) { this.attachedToEntity = (LivingEntity) entity; } } return this.attachedToEntity; } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinItemStack.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.utils.accessor.IItemStack; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ItemStack.class) public abstract class MixinItemStack implements IItemStack { @Shadow @Final private Item item; @Unique private int baritoneHash; @Shadow public abstract int getDamageValue(); private void recalculateHash() { baritoneHash = item == null ? -1 : item.hashCode() + getDamageValue(); } @Inject( method = "setDamageValue", at = @At("TAIL") ) private void onItemDamageSet(CallbackInfo ci) { recalculateHash(); } @Override public int getBaritoneHash() { // cannot do this in an init mixin because silentlib likes creating new // items in getDamageValue, which we call in recalculateHash if (baritoneHash == 0) recalculateHash(); return baritoneHash; } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinLivingEntity.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.RotationMoveEvent; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Optional; /** * @author Brady * @since 9/10/2018 */ @Mixin(LivingEntity.class) public abstract class MixinLivingEntity extends Entity { /** * Event called to override the movement direction when jumping */ @Unique private RotationMoveEvent jumpRotationEvent; @Unique private RotationMoveEvent elytraRotationEvent; private MixinLivingEntity(EntityType<?> entityTypeIn, Level worldIn) { super(entityTypeIn, worldIn); } @Inject( method = "jumpFromGround", at = @At("HEAD") ) private void preMoveRelative(CallbackInfo ci) { this.getBaritone().ifPresent(baritone -> { this.jumpRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.JUMP, this.getYRot(), this.getXRot()); baritone.getGameEventHandler().onPlayerRotationMove(this.jumpRotationEvent); }); } @Redirect( method = "jumpFromGround", at = @At( value = "INVOKE", target = "net/minecraft/world/entity/LivingEntity.getYRot()F" ) ) private float overrideYaw(LivingEntity self) { if (self instanceof LocalPlayer && BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this) != null) { return this.jumpRotationEvent.getYaw(); } return self.getYRot(); } @Inject( method = "travel", at = @At( value = "INVOKE", target = "net/minecraft/world/entity/LivingEntity.getLookAngle()Lnet/minecraft/world/phys/Vec3;" ) ) private void onPreElytraMove(Vec3 direction, CallbackInfo ci) { this.getBaritone().ifPresent(baritone -> { this.elytraRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.MOTION_UPDATE, this.getYRot(), this.getXRot()); baritone.getGameEventHandler().onPlayerRotationMove(this.elytraRotationEvent); this.setYRot(this.elytraRotationEvent.getYaw()); this.setXRot(this.elytraRotationEvent.getPitch()); }); } @Inject( method = "travel", at = @At( value = "INVOKE", target = "net/minecraft/world/entity/LivingEntity.move(Lnet/minecraft/world/entity/MoverType;Lnet/minecraft/world/phys/Vec3;)V", shift = At.Shift.AFTER ) ) private void onPostElytraMove(Vec3 direction, CallbackInfo ci) { if (this.elytraRotationEvent != null) { this.setYRot(this.elytraRotationEvent.getOriginal().getYaw()); this.setXRot(this.elytraRotationEvent.getOriginal().getPitch()); this.elytraRotationEvent = null; } } @Unique private Optional<IBaritone> getBaritone() { // noinspection ConstantConditions if (LocalPlayer.class.isInstance(this)) { return Optional.ofNullable(BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this)); } else { return Optional.empty(); } } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinLootContext.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.utils.BlockOptionalMeta; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.LootTables; import net.minecraft.world.level.storage.loot.PredicateManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(LootContext.Builder.class) public class MixinLootContext { @Redirect( method = "create", at = @At( value = "INVOKE", target = "net/minecraft/server/level/ServerLevel.getServer()Lnet/minecraft/server/MinecraftServer;" ) ) private MinecraftServer getServer(ServerLevel world) { if (world == null) { return null; } return world.getServer(); } @Redirect( method = "create", at = @At( value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getLootTables()Lnet/minecraft/world/level/storage/loot/LootTables;" ) ) private LootTables getLootTableManager(MinecraftServer server) { if (server == null) { return BlockOptionalMeta.getManager(); } return server.getLootTables(); } @Redirect( method = "create", at = @At( value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getPredicateManager()Lnet/minecraft/world/level/storage/loot/PredicateManager;" ) ) private PredicateManager getLootPredicateManager(MinecraftServer server) { if (server == null) { return BlockOptionalMeta.getPredicateManager(); } return server.getPredicateManager(); } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinMinecraft.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.TickEvent; import baritone.api.event.events.WorldEvent; import baritone.api.event.events.type.EventState; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.function.BiFunction; /** * @author Brady * @since 7/31/2018 */ @Mixin(Minecraft.class) public class MixinMinecraft { @Shadow public LocalPlayer player; @Shadow public ClientLevel level; @Unique private BiFunction<EventState, TickEvent.Type, TickEvent> tickProvider; @Inject( method = "<init>", at = @At("RETURN") ) private void postInit(CallbackInfo ci) { BaritoneAPI.getProvider().getPrimaryBaritone(); } @Inject( method = "tick", at = @At( value = "FIELD", opcode = Opcodes.GETFIELD, target = "net/minecraft/client/Minecraft.screen:Lnet/minecraft/client/gui/screens/Screen;", ordinal = 0, shift = At.Shift.BEFORE ), slice = @Slice( from = @At( value = "FIELD", opcode = Opcodes.PUTFIELD, target = "net/minecraft/client/Minecraft.missTime:I" ) ) ) private void runTick(CallbackInfo ci) { this.tickProvider = TickEvent.createNextProvider(); for (IBaritone baritone : BaritoneAPI.getProvider().getAllBaritones()) { TickEvent.Type type = baritone.getPlayerContext().player() != null && baritone.getPlayerContext().world() != null ? TickEvent.Type.IN : TickEvent.Type.OUT; baritone.getGameEventHandler().onTick(this.tickProvider.apply(EventState.PRE, type)); } } @Inject( method = "tick", at = @At("RETURN") ) private void postRunTick(CallbackInfo ci) { if (this.tickProvider == null) { return; } for (IBaritone baritone : BaritoneAPI.getProvider().getAllBaritones()) { TickEvent.Type type = baritone.getPlayerContext().player() != null && baritone.getPlayerContext().world() != null ? TickEvent.Type.IN : TickEvent.Type.OUT; baritone.getGameEventHandler().onPostTick(this.tickProvider.apply(EventState.POST, type)); } this.tickProvider = null; } @Inject( method = "tick", at = @At( value = "INVOKE", target = "net/minecraft/client/multiplayer/ClientLevel.tickEntities()V", shift = At.Shift.AFTER ) ) private void postUpdateEntities(CallbackInfo ci) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(this.player); if (baritone != null) { // Intentionally call this after all entities have been updated. That way, any modification to rotations // can be recognized by other entity code. (Fireworks and Pigs, for example) baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.POST)); } } @Inject( method = "setLevel", at = @At("HEAD") ) private void preLoadWorld(ClientLevel world, CallbackInfo ci) { // If we're unloading the world but one doesn't exist, ignore it if (this.level == null && world == null) { return; } // mc.world changing is only the primary baritone BaritoneAPI.getProvider().getPrimaryBaritone().getGameEventHandler().onWorldEvent( new WorldEvent( world, EventState.PRE ) ); } @Inject( method = "setLevel", at = @At("RETURN") ) private void postLoadWorld(ClientLevel world, CallbackInfo ci) { // still fire event for both null, as that means we've just finished exiting a world // mc.world changing is only the primary baritone BaritoneAPI.getProvider().getPrimaryBaritone().getGameEventHandler().onWorldEvent( new WorldEvent( world, EventState.POST ) ); } @Redirect( method = "tick", at = @At( value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/gui/screens/Screen;passEvents:Z" ) ) private boolean passEvents(Screen screen) { // allow user input is only the primary baritone return (BaritoneAPI.getProvider().getPrimaryBaritone().getPathingBehavior().isPathing() && player != null) || screen.passEvents; } // TODO // FIXME // bradyfix // i cant mixin // lol // https://discordapp.com/channels/208753003996512258/503692253881958400/674760939681349652 // https://discordapp.com/channels/208753003996512258/503692253881958400/674756457966862376 /*@Inject( method = "rightClickMouse", at = @At( value = "INVOKE", target = "net/minecraft/client/entity/player/ClientPlayerEntity.swingArm(Lnet/minecraft/util/Hand;)V", ordinal = 1 ), locals = LocalCapture.CAPTURE_FAILHARD ) private void onBlockUse(CallbackInfo ci, Hand var1[], int var2, int var3, Hand enumhand, ItemStack itemstack, EntityRayTraceResult rt, Entity ent, ActionResultType art, BlockRayTraceResult raytrace, int i, ActionResultType enumactionresult) { // rightClickMouse is only for the main player BaritoneAPI.getProvider().getPrimaryBaritone().getGameEventHandler().onBlockInteract(new BlockInteractEvent(raytrace.getPos(), BlockInteractEvent.Type.USE)); }*/ } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinNetworkManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.PacketEvent; import baritone.api.event.events.type.EventState; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import net.minecraft.network.Connection; import net.minecraft.network.PacketSendListener; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.PacketFlow; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** * @author Brady * @since 8/6/2018 */ @Mixin(Connection.class) public class MixinNetworkManager { @Shadow private Channel channel; @Shadow @Final private PacketFlow receiving; @Inject( method = "sendPacket", at = @At("HEAD") ) private void preDispatchPacket(Packet<?> packet, PacketSendListener packetSendListener, CallbackInfo ci) { if (this.receiving != PacketFlow.CLIENTBOUND) { return; } for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { if (ibaritone.getPlayerContext().player() != null && ibaritone.getPlayerContext().player().connection.getConnection() == (Connection) (Object) this) { ibaritone.getGameEventHandler().onSendPacket(new PacketEvent((Connection) (Object) this, EventState.PRE, packet)); } } } @Inject( method = "sendPacket", at = @At("RETURN") ) private void postDispatchPacket(Packet<?> packet, PacketSendListener packetSendListener, CallbackInfo ci) { if (this.receiving != PacketFlow.CLIENTBOUND) { return; } for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { if (ibaritone.getPlayerContext().player() != null && ibaritone.getPlayerContext().player().connection.getConnection() == (Connection) (Object) this) { ibaritone.getGameEventHandler().onSendPacket(new PacketEvent((Connection) (Object) this, EventState.POST, packet)); } } } @Inject( method = "channelRead0", at = @At( value = "INVOKE", target = "net/minecraft/network/Connection.genericsFtw(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;)V" ) ) private void preProcessPacket(ChannelHandlerContext context, Packet<?> packet, CallbackInfo ci) { if (this.receiving != PacketFlow.CLIENTBOUND) { return; } for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { if (ibaritone.getPlayerContext().player() != null && ibaritone.getPlayerContext().player().connection.getConnection() == (Connection) (Object) this) { ibaritone.getGameEventHandler().onReceivePacket(new PacketEvent((Connection) (Object) this, EventState.PRE, packet)); } } } @Inject( method = "channelRead0", at = @At("RETURN") ) private void postProcessPacket(ChannelHandlerContext context, Packet<?> packet, CallbackInfo ci) { if (!this.channel.isOpen() || this.receiving != PacketFlow.CLIENTBOUND) { return; } for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { if (ibaritone.getPlayerContext().player() != null && ibaritone.getPlayerContext().player().connection.getConnection() == (Connection) (Object) this) { ibaritone.getGameEventHandler().onReceivePacket(new PacketEvent((Connection) (Object) this, EventState.POST, packet)); } } } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinPalettedContainer$Data.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.utils.accessor.IPalettedContainer.IData; import net.minecraft.util.BitStorage; import net.minecraft.world.level.chunk.Palette; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(targets = "net/minecraft/world/level/chunk/PalettedContainer$Data") public abstract class MixinPalettedContainer$Data<T> implements IData<T> { @Accessor public abstract Palette<T> getPalette(); @Accessor public abstract BitStorage getStorage(); } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinPalettedContainer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.utils.accessor.IPalettedContainer; import baritone.utils.accessor.IPalettedContainer.IData; import net.minecraft.util.BitStorage; import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @Mixin(PalettedContainer.class) public abstract class MixinPalettedContainer<T> implements IPalettedContainer<T> { private static final MethodHandle DATA_GETTER; // Mixin has no way of referring to the data field and we can't use inheritance // tricks to determine its name, so we use this ugly workaround instead. // Classloading is hell here and causes accessor mixins (@Mixin interfaces with // only @Accessor and @Invoker methods) to break on use and proguard hates method // handles and on top of that mojang decided that error messages during world // load are not needed so if you want to debug this you'll probably need an extra // mixin just to display the error and hard quit the game before follow up errors // blow up your log file. // Mumphrey, please add the shadow classes you promised 5 years ago. static { Field dataField = null; for (Field field : PalettedContainer.class.getDeclaredFields()) { Class<?> fieldType = field.getType(); if (IData.class.isAssignableFrom(fieldType)) { if ((field.getModifiers() & (Modifier.STATIC | Modifier.FINAL)) != 0 || field.isSynthetic()) { continue; } if (dataField != null) { throw new IllegalStateException("PalettedContainer has more than one Data field."); } dataField = field; } } if (dataField == null) { throw new IllegalStateException("PalettedContainer has no Data field."); } MethodHandle rawGetter; try { rawGetter = MethodHandles.lookup().unreflectGetter(dataField); } catch (IllegalAccessException impossible) { // we literally are the owning class, wtf? throw new IllegalStateException("PalettedContainer may not access its own field?!", impossible); } MethodType getterType = MethodType.methodType(IData.class, PalettedContainer.class); DATA_GETTER = MethodHandles.explicitCastArguments(rawGetter, getterType); } @Override public Palette<T> getPalette() { return data().getPalette(); } @Override public BitStorage getStorage() { return data().getStorage(); } @Unique private IData<T> data() { try { // cast to Object first so the method handle doesn't hide the interface usage from proguard return (IData<T>) (Object) DATA_GETTER.invoke((PalettedContainer<T>) (Object) this); } catch (Throwable t) { throw sneaky(t, RuntimeException.class); } } @Unique private static <T extends Throwable> T sneaky(Throwable t, Class<T> as) throws T { throw (T) t; } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinPlayerController.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.utils.accessor.IPlayerControllerMP; import net.minecraft.client.multiplayer.MultiPlayerGameMode; import net.minecraft.core.BlockPos; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(MultiPlayerGameMode.class) public abstract class MixinPlayerController implements IPlayerControllerMP { @Accessor("isDestroying") @Override public abstract void setIsHittingBlock(boolean isHittingBlock); @Accessor("isDestroying") @Override public abstract boolean isHittingBlock(); @Accessor("destroyBlockPos") @Override public abstract BlockPos getCurrentBlock(); @Invoker("ensureHasSentCarriedItem") @Override public abstract void callSyncCurrentPlayItem(); @Accessor("destroyDelay") @Override public abstract void setDestroyDelay(int destroyDelay); } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinScreen.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.ChatEvent; import baritone.utils.accessor.IGuiScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Style; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.net.URI; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; @Mixin(Screen.class) public abstract class MixinScreen implements IGuiScreen { @Override @Invoker("openLink") public abstract void openLinkInvoker(URI url); //TODO: switch to enum extention with mixin 9.0 or whenever Mumfrey gets around to it @Inject(at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;)V", remap = false, ordinal = 1), method = "handleComponentClicked", cancellable = true) public void handleCustomClickEvent(Style style, CallbackInfoReturnable<Boolean> cir) { ClickEvent clickEvent = style.getClickEvent(); if (clickEvent == null) { return; } String command = clickEvent.getValue(); if (command == null || !command.startsWith(FORCE_COMMAND_PREFIX)) { return; } IBaritone baritone = BaritoneAPI.getProvider().getPrimaryBaritone(); if (baritone != null) { baritone.getGameEventHandler().onSendChatMessage(new ChatEvent(command)); } cir.setReturnValue(true); cir.cancel(); } } ================================================ FILE: src/launch/java/baritone/launch/mixins/MixinWorldRenderer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.RenderEvent; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Camera; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; /** * @author Brady * @since 2/13/2020 */ @Mixin(LevelRenderer.class) public class MixinWorldRenderer { @Inject( method = "renderLevel", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILSOFT ) private void onStartHand(PoseStack matrixStackIn, float partialTicks, long finishTimeNano, boolean drawBlockOutline, Camera activeRenderInfoIn, GameRenderer gameRendererIn, LightTexture lightmapIn, Matrix4f projectionIn, CallbackInfo ci) { for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { ibaritone.getGameEventHandler().onRenderPass(new RenderEvent(partialTicks, matrixStackIn, projectionIn)); } } } ================================================ FILE: src/launch/resources/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 MixinConfigs: mixins.baritone.json MixinConnector: baritone.launch.BaritoneMixinConnector Implementation-Title: Baritone Implementation-Version: version ================================================ FILE: src/launch/resources/mixins.baritone.json ================================================ { "required": true, "package": "baritone.launch.mixins", "compatibilityLevel": "JAVA_17", "verbose": false, "injectors": { "maxShiftBy": 2, "defaultRequire": 1 }, "client": [ "MixinChunkArray", "MixinClientChunkProvider", "MixinClientPlayerEntity", "MixinClientPlayNetHandler", "MixinCommandSuggestionHelper", "MixinEntity", "MixinEntityRenderManager", "MixinFireworkRocketEntity", "MixinItemStack", "MixinLivingEntity", "MixinLootContext", "MixinMinecraft", "MixinNetworkManager", "MixinPalettedContainer", "MixinPalettedContainer$Data", "MixinPlayerController", "MixinScreen", "MixinWorldRenderer" ] } ================================================ FILE: src/main/java/baritone/Baritone.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.Settings; import baritone.api.behavior.IBehavior; import baritone.api.event.listener.IEventBus; import baritone.api.process.IBaritoneProcess; import baritone.api.process.IElytraProcess; import baritone.api.utils.IPlayerContext; import baritone.behavior.*; import baritone.cache.WorldProvider; import baritone.command.manager.CommandManager; import baritone.event.GameEventHandler; import baritone.process.*; import baritone.selection.SelectionManager; import baritone.utils.BlockStateInterface; import baritone.utils.GuiClick; import baritone.utils.InputOverrideHandler; import baritone.utils.PathingControlManager; import baritone.utils.player.BaritonePlayerContext; import net.minecraft.client.Minecraft; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Function; /** * @author Brady * @since 7/31/2018 */ public class Baritone implements IBaritone { private static final ThreadPoolExecutor threadPool; static { threadPool = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); } private final Minecraft mc; private final Path directory; private final GameEventHandler gameEventHandler; private final PathingBehavior pathingBehavior; private final LookBehavior lookBehavior; private final InventoryBehavior inventoryBehavior; private final InputOverrideHandler inputOverrideHandler; private final FollowProcess followProcess; private final MineProcess mineProcess; private final GetToBlockProcess getToBlockProcess; private final CustomGoalProcess customGoalProcess; private final BuilderProcess builderProcess; private final ExploreProcess exploreProcess; private final FarmProcess farmProcess; private final InventoryPauserProcess inventoryPauserProcess; private final IElytraProcess elytraProcess; private final PathingControlManager pathingControlManager; private final SelectionManager selectionManager; private final CommandManager commandManager; private final IPlayerContext playerContext; private final WorldProvider worldProvider; public BlockStateInterface bsi; Baritone(Minecraft mc) { this.mc = mc; this.gameEventHandler = new GameEventHandler(this); this.directory = mc.gameDirectory.toPath().resolve("baritone"); if (!Files.exists(this.directory)) { try { Files.createDirectories(this.directory); } catch (IOException ignored) {} } // Define this before behaviors try and get it, or else it will be null and the builds will fail! this.playerContext = new BaritonePlayerContext(this, mc); { this.lookBehavior = this.registerBehavior(LookBehavior::new); this.pathingBehavior = this.registerBehavior(PathingBehavior::new); this.inventoryBehavior = this.registerBehavior(InventoryBehavior::new); this.inputOverrideHandler = this.registerBehavior(InputOverrideHandler::new); this.registerBehavior(WaypointBehavior::new); } this.pathingControlManager = new PathingControlManager(this); { this.followProcess = this.registerProcess(FollowProcess::new); this.mineProcess = this.registerProcess(MineProcess::new); this.customGoalProcess = this.registerProcess(CustomGoalProcess::new); // very high iq this.getToBlockProcess = this.registerProcess(GetToBlockProcess::new); this.builderProcess = this.registerProcess(BuilderProcess::new); this.exploreProcess = this.registerProcess(ExploreProcess::new); this.farmProcess = this.registerProcess(FarmProcess::new); this.inventoryPauserProcess = this.registerProcess(InventoryPauserProcess::new); this.elytraProcess = this.registerProcess(ElytraProcess::create); this.registerProcess(BackfillProcess::new); } this.worldProvider = new WorldProvider(this); this.selectionManager = new SelectionManager(this); this.commandManager = new CommandManager(this); } public void registerBehavior(IBehavior behavior) { this.gameEventHandler.registerEventListener(behavior); } public <T extends IBehavior> T registerBehavior(Function<Baritone, T> constructor) { final T behavior = constructor.apply(this); this.registerBehavior(behavior); return behavior; } public <T extends IBaritoneProcess> T registerProcess(Function<Baritone, T> constructor) { final T behavior = constructor.apply(this); this.pathingControlManager.registerProcess(behavior); return behavior; } @Override public PathingControlManager getPathingControlManager() { return this.pathingControlManager; } @Override public InputOverrideHandler getInputOverrideHandler() { return this.inputOverrideHandler; } @Override public CustomGoalProcess getCustomGoalProcess() { return this.customGoalProcess; } @Override public GetToBlockProcess getGetToBlockProcess() { return this.getToBlockProcess; } @Override public IPlayerContext getPlayerContext() { return this.playerContext; } @Override public FollowProcess getFollowProcess() { return this.followProcess; } @Override public BuilderProcess getBuilderProcess() { return this.builderProcess; } public InventoryBehavior getInventoryBehavior() { return this.inventoryBehavior; } @Override public LookBehavior getLookBehavior() { return this.lookBehavior; } @Override public ExploreProcess getExploreProcess() { return this.exploreProcess; } @Override public MineProcess getMineProcess() { return this.mineProcess; } @Override public FarmProcess getFarmProcess() { return this.farmProcess; } public InventoryPauserProcess getInventoryPauserProcess() { return this.inventoryPauserProcess; } @Override public PathingBehavior getPathingBehavior() { return this.pathingBehavior; } @Override public SelectionManager getSelectionManager() { return selectionManager; } @Override public WorldProvider getWorldProvider() { return this.worldProvider; } @Override public IEventBus getGameEventHandler() { return this.gameEventHandler; } @Override public CommandManager getCommandManager() { return this.commandManager; } @Override public IElytraProcess getElytraProcess() { return this.elytraProcess; } @Override public void openClick() { new Thread(() -> { try { Thread.sleep(100); mc.execute(() -> mc.setScreen(new GuiClick())); } catch (Exception ignored) {} }).start(); } public Path getDirectory() { return this.directory; } public static Settings settings() { return BaritoneAPI.getSettings(); } public static Executor getExecutor() { return threadPool; } } ================================================ FILE: src/main/java/baritone/BaritoneProvider.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone; import baritone.api.IBaritone; import baritone.api.IBaritoneProvider; import baritone.api.cache.IWorldScanner; import baritone.api.command.ICommandSystem; import baritone.api.schematic.ISchematicSystem; import baritone.cache.FasterWorldScanner; import baritone.command.CommandSystem; import baritone.command.ExampleBaritoneControl; import baritone.utils.schematic.SchematicSystem; import net.minecraft.client.Minecraft; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * @author Brady * @since 9/29/2018 */ public final class BaritoneProvider implements IBaritoneProvider { private final List<IBaritone> all; private final List<IBaritone> allView; public BaritoneProvider() { this.all = new CopyOnWriteArrayList<>(); this.allView = Collections.unmodifiableList(this.all); // Setup chat control, just for the primary instance final Baritone primary = (Baritone) this.createBaritone(Minecraft.getInstance()); primary.registerBehavior(ExampleBaritoneControl::new); } @Override public IBaritone getPrimaryBaritone() { return this.all.get(0); } @Override public List<IBaritone> getAllBaritones() { return this.allView; } @Override public synchronized IBaritone createBaritone(Minecraft minecraft) { IBaritone baritone = this.getBaritoneForMinecraft(minecraft); if (baritone == null) { this.all.add(baritone = new Baritone(minecraft)); } return baritone; } @Override public synchronized boolean destroyBaritone(IBaritone baritone) { return baritone != this.getPrimaryBaritone() && this.all.remove(baritone); } @Override public IWorldScanner getWorldScanner() { return FasterWorldScanner.INSTANCE; } @Override public ICommandSystem getCommandSystem() { return CommandSystem.INSTANCE; } @Override public ISchematicSystem getSchematicSystem() { return SchematicSystem.INSTANCE; } } ================================================ FILE: src/main/java/baritone/KeepName.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone; // Annotation for classes and class members that should not be renamed by proguard public @interface KeepName {} ================================================ FILE: src/main/java/baritone/behavior/Behavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.behavior; import baritone.Baritone; import baritone.api.behavior.IBehavior; import baritone.api.utils.IPlayerContext; /** * A type of game event listener that is given {@link Baritone} instance context. * * @author Brady * @since 8/1/2018 */ public class Behavior implements IBehavior { public final Baritone baritone; public final IPlayerContext ctx; protected Behavior(Baritone baritone) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); } } ================================================ FILE: src/main/java/baritone/behavior/InventoryBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.behavior; import baritone.Baritone; import baritone.api.event.events.TickEvent; import baritone.api.utils.Helper; import baritone.utils.ToolSet; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.Direction; import net.minecraft.core.NonNullList; import net.minecraft.world.InteractionHand; import net.minecraft.world.inventory.ClickType; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.DiggerItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.PickaxeItem; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import java.util.ArrayList; import java.util.OptionalInt; import java.util.Random; import java.util.function.Predicate; public final class InventoryBehavior extends Behavior implements Helper { int ticksSinceLastInventoryMove; int[] lastTickRequestedMove; // not everything asks every tick, so remember the request while coming to a halt public InventoryBehavior(Baritone baritone) { super(baritone); } @Override public void onTick(TickEvent event) { if (!Baritone.settings().allowInventory.value) { return; } if (event.getType() == TickEvent.Type.OUT) { return; } if (ctx.player().containerMenu != ctx.player().inventoryMenu) { // we have a crafting table or a chest or something open return; } ticksSinceLastInventoryMove++; if (firstValidThrowaway() >= 9) { // aka there are none on the hotbar, but there are some in main inventory requestSwapWithHotBar(firstValidThrowaway(), 8); } int pick = bestToolAgainst(Blocks.STONE, PickaxeItem.class); if (pick >= 9) { requestSwapWithHotBar(pick, 0); } if (lastTickRequestedMove != null) { logDebug("Remembering to move " + lastTickRequestedMove[0] + " " + lastTickRequestedMove[1] + " from a previous tick"); requestSwapWithHotBar(lastTickRequestedMove[0], lastTickRequestedMove[1]); } } public boolean attemptToPutOnHotbar(int inMainInvy, Predicate<Integer> disallowedHotbar) { OptionalInt destination = getTempHotbarSlot(disallowedHotbar); if (destination.isPresent()) { if (!requestSwapWithHotBar(inMainInvy, destination.getAsInt())) { return false; } } return true; } public OptionalInt getTempHotbarSlot(Predicate<Integer> disallowedHotbar) { // we're using 0 and 8 for pickaxe and throwaway ArrayList<Integer> candidates = new ArrayList<>(); for (int i = 1; i < 8; i++) { if (ctx.player().getInventory().items.get(i).isEmpty() && !disallowedHotbar.test(i)) { candidates.add(i); } } if (candidates.isEmpty()) { for (int i = 1; i < 8; i++) { if (!disallowedHotbar.test(i)) { candidates.add(i); } } } if (candidates.isEmpty()) { return OptionalInt.empty(); } return OptionalInt.of(candidates.get(new Random().nextInt(candidates.size()))); } private boolean requestSwapWithHotBar(int inInventory, int inHotbar) { lastTickRequestedMove = new int[]{inInventory, inHotbar}; if (ticksSinceLastInventoryMove < Baritone.settings().ticksBetweenInventoryMoves.value) { logDebug("Inventory move requested but delaying " + ticksSinceLastInventoryMove + " " + Baritone.settings().ticksBetweenInventoryMoves.value); return false; } if (Baritone.settings().inventoryMoveOnlyIfStationary.value && !baritone.getInventoryPauserProcess().stationaryForInventoryMove()) { logDebug("Inventory move requested but delaying until stationary"); return false; } ctx.playerController().windowClick(ctx.player().inventoryMenu.containerId, inInventory < 9 ? inInventory + 36 : inInventory, inHotbar, ClickType.SWAP, ctx.player()); ticksSinceLastInventoryMove = 0; lastTickRequestedMove = null; return true; } private int firstValidThrowaway() { // TODO offhand idk NonNullList<ItemStack> invy = ctx.player().getInventory().items; for (int i = 0; i < invy.size(); i++) { if (Baritone.settings().acceptableThrowawayItems.value.contains(invy.get(i).getItem())) { return i; } } return -1; } private int bestToolAgainst(Block against, Class<? extends DiggerItem> cla$$) { NonNullList<ItemStack> invy = ctx.player().getInventory().items; int bestInd = -1; double bestSpeed = -1; for (int i = 0; i < invy.size(); i++) { ItemStack stack = invy.get(i); if (stack.isEmpty()) { continue; } if (Baritone.settings().itemSaver.value && (stack.getDamageValue() + Baritone.settings().itemSaverThreshold.value) >= stack.getMaxDamage() && stack.getMaxDamage() > 1) { continue; } if (cla$$.isInstance(stack.getItem())) { double speed = ToolSet.calculateSpeedVsBlock(stack, against.defaultBlockState()); // takes into account enchants if (speed > bestSpeed) { bestSpeed = speed; bestInd = i; } } } return bestInd; } public boolean hasGenericThrowaway() { for (Item item : Baritone.settings().acceptableThrowawayItems.value) { if (throwaway(false, stack -> item.equals(stack.getItem()))) { return true; } } return false; } public boolean selectThrowawayForLocation(boolean select, int x, int y, int z) { BlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z)); if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof BlockItem && maybe.equals(((BlockItem) stack.getItem()).getBlock().getStateForPlacement(new BlockPlaceContext(new UseOnContext(ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, new BlockHitResult(new Vec3(ctx.player().position().x, ctx.player().position().y, ctx.player().position().z), Direction.UP, ctx.playerFeet(), false)) {}))))) { return true; // gotem } if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof BlockItem && ((BlockItem) stack.getItem()).getBlock().equals(maybe.getBlock()))) { return true; } for (Item item : Baritone.settings().acceptableThrowawayItems.value) { if (throwaway(select, stack -> item.equals(stack.getItem()))) { return true; } } return false; } public boolean throwaway(boolean select, Predicate<? super ItemStack> desired) { return throwaway(select, desired, Baritone.settings().allowInventory.value); } public boolean throwaway(boolean select, Predicate<? super ItemStack> desired, boolean allowInventory) { LocalPlayer p = ctx.player(); NonNullList<ItemStack> inv = p.getInventory().items; for (int i = 0; i < 9; i++) { ItemStack item = inv.get(i); // this usage of settings() is okay because it's only called once during pathing // (while creating the CalculationContext at the very beginning) // and then it's called during execution // since this function is never called during cost calculation, we don't need to migrate // acceptableThrowawayItems to the CalculationContext if (desired.test(item)) { if (select) { p.getInventory().selected = i; } return true; } } if (desired.test(p.getInventory().offhand.get(0))) { // main hand takes precedence over off hand // that means that if we have block A selected in main hand and block B in off hand, right clicking places block B // we've already checked above ^ and the main hand can't possible have an acceptablethrowawayitem // so we need to select in the main hand something that doesn't right click // so not a shovel, not a hoe, not a block, etc for (int i = 0; i < 9; i++) { ItemStack item = inv.get(i); if (item.isEmpty() || item.getItem() instanceof PickaxeItem) { if (select) { p.getInventory().selected = i; } return true; } } } if (allowInventory) { for (int i = 9; i < 36; i++) { if (desired.test(inv.get(i))) { if (select) { requestSwapWithHotBar(i, 7); p.getInventory().selected = 7; } return true; } } } return false; } } ================================================ FILE: src/main/java/baritone/behavior/LookBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.behavior; import baritone.Baritone; import baritone.api.Settings; import baritone.api.behavior.ILookBehavior; import baritone.api.behavior.look.IAimProcessor; import baritone.api.behavior.look.ITickableAimProcessor; import baritone.api.event.events.*; import baritone.api.utils.IPlayerContext; import baritone.api.utils.Rotation; import baritone.behavior.look.ForkableRandom; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import java.util.ArrayDeque; import java.util.Deque; import java.util.Optional; public final class LookBehavior extends Behavior implements ILookBehavior { /** * The current look target, may be {@code null}. */ private Target target; /** * The rotation known to the server. Returned by {@link #getEffectiveRotation()} for use in {@link IPlayerContext}. */ private Rotation serverRotation; /** * The last player rotation. Used to restore the player's angle when using free look. * * @see Settings#freeLook */ private Rotation prevRotation; private final AimProcessor processor; private final Deque<Float> smoothYawBuffer; private final Deque<Float> smoothPitchBuffer; public LookBehavior(Baritone baritone) { super(baritone); this.processor = new AimProcessor(baritone.getPlayerContext()); this.smoothYawBuffer = new ArrayDeque<>(); this.smoothPitchBuffer = new ArrayDeque<>(); } @Override public void updateTarget(Rotation rotation, boolean blockInteract) { this.target = new Target(rotation, Target.Mode.resolve(ctx, blockInteract)); } @Override public IAimProcessor getAimProcessor() { return this.processor; } @Override public void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.IN) { this.processor.tick(); } } @Override public void onPlayerUpdate(PlayerUpdateEvent event) { if (this.target == null) { return; } switch (event.getState()) { case PRE: { if (this.target.mode == Target.Mode.NONE) { // Just return for PRE, we still want to set target to null on POST return; } this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); final Rotation actual = this.processor.peekRotation(this.target.rotation); ctx.player().setYRot(actual.getYaw()); ctx.player().setXRot(actual.getPitch()); break; } case POST: { // Reset the player's rotations back to their original values if (this.prevRotation != null) { this.smoothYawBuffer.addLast(this.target.rotation.getYaw()); while (this.smoothYawBuffer.size() > Baritone.settings().smoothLookTicks.value) { this.smoothYawBuffer.removeFirst(); } this.smoothPitchBuffer.addLast(this.target.rotation.getPitch()); while (this.smoothPitchBuffer.size() > Baritone.settings().smoothLookTicks.value) { this.smoothPitchBuffer.removeFirst(); } if (this.target.mode == Target.Mode.SERVER) { ctx.player().setYRot(this.prevRotation.getYaw()); ctx.player().setXRot(this.prevRotation.getPitch()); } else if (ctx.player().isFallFlying() ? Baritone.settings().elytraSmoothLook.value : Baritone.settings().smoothLook.value) { ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); if (ctx.player().isFallFlying()) { ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); } } //ctx.player().xRotO = prevRotation.getPitch(); //ctx.player().yRotO = prevRotation.getYaw(); this.prevRotation = null; } // The target is done being used for this game tick, so it can be invalidated this.target = null; break; } default: break; } } @Override public void onSendPacket(PacketEvent event) { if (!(event.getPacket() instanceof ServerboundMovePlayerPacket)) { return; } final ServerboundMovePlayerPacket packet = (ServerboundMovePlayerPacket) event.getPacket(); if (packet instanceof ServerboundMovePlayerPacket.Rot || packet instanceof ServerboundMovePlayerPacket.PosRot) { this.serverRotation = new Rotation(packet.getYRot(0.0f), packet.getXRot(0.0f)); } } @Override public void onWorldEvent(WorldEvent event) { this.serverRotation = null; this.target = null; } public void pig() { if (this.target != null) { final Rotation actual = this.processor.peekRotation(this.target.rotation); ctx.player().setYRot(actual.getYaw()); } } public Optional<Rotation> getEffectiveRotation() { if (Baritone.settings().freeLook.value) { return Optional.ofNullable(this.serverRotation); } // If freeLook isn't on, just defer to the player's actual rotations return Optional.empty(); } @Override public void onPlayerRotationMove(RotationMoveEvent event) { if (this.target != null) { final Rotation actual = this.processor.peekRotation(this.target.rotation); event.setYaw(actual.getYaw()); event.setPitch(actual.getPitch()); } } private static final class AimProcessor extends AbstractAimProcessor { public AimProcessor(final IPlayerContext ctx) { super(ctx); } @Override protected Rotation getPrevRotation() { // Implementation will use LookBehavior.serverRotation return ctx.playerRotations(); } } private static abstract class AbstractAimProcessor implements ITickableAimProcessor { protected final IPlayerContext ctx; private final ForkableRandom rand; private double randomYawOffset; private double randomPitchOffset; public AbstractAimProcessor(IPlayerContext ctx) { this.ctx = ctx; this.rand = new ForkableRandom(); } private AbstractAimProcessor(final AbstractAimProcessor source) { this.ctx = source.ctx; this.rand = source.rand.fork(); this.randomYawOffset = source.randomYawOffset; this.randomPitchOffset = source.randomPitchOffset; } @Override public final Rotation peekRotation(final Rotation rotation) { final Rotation prev = this.getPrevRotation(); float desiredYaw = rotation.getYaw(); float desiredPitch = rotation.getPitch(); // In other words, the target doesn't care about the pitch, so it used playerRotations().getPitch() // and it's safe to adjust it to a normal level if (desiredPitch == prev.getPitch()) { desiredPitch = nudgeToLevel(desiredPitch); } desiredYaw += this.randomYawOffset; desiredPitch += this.randomPitchOffset; return new Rotation( this.calculateMouseMove(prev.getYaw(), desiredYaw), this.calculateMouseMove(prev.getPitch(), desiredPitch) ).clamp(); } @Override public final void tick() { // randomLooking this.randomYawOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; this.randomPitchOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; // randomLooking113 double random = this.rand.nextDouble() - 0.5; if (Math.abs(random) < 0.1) { random *= 4; } this.randomYawOffset += random * Baritone.settings().randomLooking113.value; } @Override public final void advance(int ticks) { for (int i = 0; i < ticks; i++) { this.tick(); } } @Override public Rotation nextRotation(final Rotation rotation) { final Rotation actual = this.peekRotation(rotation); this.tick(); return actual; } @Override public final ITickableAimProcessor fork() { return new AbstractAimProcessor(this) { private Rotation prev = AbstractAimProcessor.this.getPrevRotation(); @Override public Rotation nextRotation(final Rotation rotation) { return (this.prev = super.nextRotation(rotation)); } @Override protected Rotation getPrevRotation() { return this.prev; } }; } protected abstract Rotation getPrevRotation(); /** * Nudges the player's pitch to a regular level. (Between {@code -20} and {@code 10}, increments are by {@code 1}) */ private float nudgeToLevel(float pitch) { if (pitch < -20) { return pitch + 1; } else if (pitch > 10) { return pitch - 1; } return pitch; } private float calculateMouseMove(float current, float target) { final float delta = target - current; final double deltaPx = angleToMouse(delta); // yes, even the mouse movements use double return current + mouseToAngle(deltaPx); } private double angleToMouse(float angleDelta) { final float minAngleChange = mouseToAngle(1); return Math.round(angleDelta / minAngleChange); } private float mouseToAngle(double mouseDelta) { // casting float literals to double gets us the precise values used by mc final double f = ctx.minecraft().options.sensitivity().get() * (double) 0.6f + (double) 0.2f; return (float) (mouseDelta * f * f * f * 8.0d) * 0.15f; // yes, one double and one float scaling factor } } private static class Target { public final Rotation rotation; public final Mode mode; public Target(Rotation rotation, Mode mode) { this.rotation = rotation; this.mode = mode; } enum Mode { /** * Rotation will be set client-side and is visual to the player */ CLIENT, /** * Rotation will be set server-side and is silent to the player */ SERVER, /** * Rotation will remain unaffected on both the client and server */ NONE; static Mode resolve(IPlayerContext ctx, boolean blockInteract) { final Settings settings = Baritone.settings(); final boolean antiCheat = settings.antiCheatCompatibility.value; final boolean blockFreeLook = settings.blockFreeLook.value; if (ctx.player().isFallFlying()) { // always need to set angles while flying return settings.elytraFreeLook.value ? SERVER : CLIENT; } else if (settings.freeLook.value) { // Regardless of if antiCheatCompatibility is enabled, if a blockInteract is requested then the player // rotation needs to be set somehow, otherwise Baritone will halt since objectMouseOver() will just be // whatever the player is mousing over visually. Let's just settle for setting it silently. if (blockInteract) { return blockFreeLook ? SERVER : CLIENT; } return antiCheat ? SERVER : NONE; } // all freeLook settings are disabled so set the angles return CLIENT; } } } } ================================================ FILE: src/main/java/baritone/behavior/PathingBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.behavior; import baritone.Baritone; import baritone.api.behavior.IPathingBehavior; import baritone.api.event.events.*; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalXZ; import baritone.api.process.PathingCommand; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; import baritone.api.utils.PathCalculationResult; import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.pathing.calc.AStarPathFinder; import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; import baritone.pathing.path.PathExecutor; import baritone.process.ElytraProcess; import baritone.utils.PathRenderer; import baritone.utils.PathingCommandContext; import baritone.utils.pathing.Favoring; import java.util.ArrayList; import java.util.Comparator; import java.util.Objects; import java.util.Optional; import java.util.concurrent.LinkedBlockingQueue; import net.minecraft.core.BlockPos; public final class PathingBehavior extends Behavior implements IPathingBehavior, Helper { private PathExecutor current; private PathExecutor next; private Goal goal; private CalculationContext context; /*eta*/ private int ticksElapsedSoFar; private BetterBlockPos startPosition; private boolean safeToCancel; private boolean pauseRequestedLastTick; private boolean unpausedLastTick; private boolean pausedThisTick; private boolean cancelRequested; private boolean calcFailedLastTick; private volatile AbstractNodeCostSearch inProgress; private final Object pathCalcLock = new Object(); private final Object pathPlanLock = new Object(); private boolean lastAutoJump; private BetterBlockPos expectedSegmentStart; private final LinkedBlockingQueue<PathEvent> toDispatch = new LinkedBlockingQueue<>(); public PathingBehavior(Baritone baritone) { super(baritone); } private void queuePathEvent(PathEvent event) { toDispatch.add(event); } private void dispatchEvents() { ArrayList<PathEvent> curr = new ArrayList<>(); toDispatch.drainTo(curr); calcFailedLastTick = curr.contains(PathEvent.CALC_FAILED); for (PathEvent event : curr) { baritone.getGameEventHandler().onPathEvent(event); } } @Override public void onTick(TickEvent event) { dispatchEvents(); if (event.getType() == TickEvent.Type.OUT) { secretInternalSegmentCancel(); baritone.getPathingControlManager().cancelEverything(); return; } expectedSegmentStart = pathStart(); baritone.getPathingControlManager().preTick(); tickPath(); ticksElapsedSoFar++; dispatchEvents(); } @Override public void onPlayerSprintState(SprintStateEvent event) { if (isPathing()) { event.setState(current.isSprinting()); } } private void tickPath() { pausedThisTick = false; if (pauseRequestedLastTick && safeToCancel) { pauseRequestedLastTick = false; if (unpausedLastTick) { baritone.getInputOverrideHandler().clearAllKeys(); baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); } unpausedLastTick = false; pausedThisTick = true; return; } unpausedLastTick = true; if (cancelRequested) { cancelRequested = false; baritone.getInputOverrideHandler().clearAllKeys(); } synchronized (pathPlanLock) { synchronized (pathCalcLock) { if (inProgress != null) { // we are calculating // are we calculating the right thing though? 🤔 BetterBlockPos calcFrom = inProgress.getStart(); Optional<IPath> currentBest = inProgress.bestPathSoFar(); if ((current == null || !current.getPath().getDest().equals(calcFrom)) // if current ends in inProgress's start, then we're ok && !calcFrom.equals(ctx.playerFeet()) && !calcFrom.equals(expectedSegmentStart) // if current starts in our playerFeet or pathStart, then we're ok && (!currentBest.isPresent() || (!currentBest.get().positions().contains(ctx.playerFeet()) && !currentBest.get().positions().contains(expectedSegmentStart))) // if ) { // when it was *just* started, currentBest will be empty so we need to also check calcFrom since that's always present inProgress.cancel(); // cancellation doesn't dispatch any events } } } if (current == null) { return; } safeToCancel = current.onTick(); if (current.failed() || current.finished()) { current = null; if (goal == null || goal.isInGoal(ctx.playerFeet())) { logDebug("All done. At " + goal); queuePathEvent(PathEvent.AT_GOAL); next = null; if (Baritone.settings().disconnectOnArrival.value) { ctx.world().disconnect(); } return; } if (next != null && !next.getPath().positions().contains(ctx.playerFeet()) && !next.getPath().positions().contains(expectedSegmentStart)) { // can contain either one // if the current path failed, we may not actually be on the next one, so make sure logDebug("Discarding next path as it does not contain current position"); // for example if we had a nicely planned ahead path that starts where current ends // that's all fine and good // but if we fail in the middle of current // we're nowhere close to our planned ahead path // so need to discard it sadly. queuePathEvent(PathEvent.DISCARD_NEXT); next = null; } if (next != null) { logDebug("Continuing on to planned next path"); queuePathEvent(PathEvent.CONTINUING_ONTO_PLANNED_NEXT); current = next; next = null; current.onTick(); // don't waste a tick doing nothing, get started right away return; } // at this point, current just ended, but we aren't in the goal and have no plan for the future synchronized (pathCalcLock) { if (inProgress != null) { queuePathEvent(PathEvent.PATH_FINISHED_NEXT_STILL_CALCULATING); return; } // we aren't calculating queuePathEvent(PathEvent.CALC_STARTED); findPathInNewThread(expectedSegmentStart, true, context); } return; } // at this point, we know current is in progress if (safeToCancel && next != null && next.snipsnapifpossible()) { // a movement just ended; jump directly onto the next path logDebug("Splicing into planned next path early..."); queuePathEvent(PathEvent.SPLICING_ONTO_NEXT_EARLY); current = next; next = null; current.onTick(); return; } if (Baritone.settings().splicePath.value) { current = current.trySplice(next); } if (next != null && current.getPath().getDest().equals(next.getPath().getDest())) { next = null; } synchronized (pathCalcLock) { if (inProgress != null) { // if we aren't calculating right now return; } if (next != null) { // and we have no plan for what to do next return; } if (goal == null || goal.isInGoal(current.getPath().getDest())) { // and this path doesn't get us all the way there return; } if (ticksRemainingInSegment(false).get() < Baritone.settings().planningTickLookahead.value) { // and this path has 7.5 seconds or less left // don't include the current movement so a very long last movement (e.g. descend) doesn't trip it up // if we actually included current, it wouldn't start planning ahead until the last movement was done, if the last movement took more than 7.5 seconds on its own logDebug("Path almost over. Planning ahead..."); queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_STARTED); findPathInNewThread(current.getPath().getDest(), false, context); } } } } @Override public void onPlayerUpdate(PlayerUpdateEvent event) { if (current != null) { switch (event.getState()) { case PRE: lastAutoJump = ctx.minecraft().options.autoJump().get(); ctx.minecraft().options.autoJump().set(false); break; case POST: ctx.minecraft().options.autoJump().set(lastAutoJump); break; default: break; } } } public void secretInternalSetGoal(Goal goal) { this.goal = goal; } public boolean secretInternalSetGoalAndPath(PathingCommand command) { secretInternalSetGoal(command.goal); if (command instanceof PathingCommandContext) { context = ((PathingCommandContext) command).desiredCalcContext; } else { context = new CalculationContext(baritone, true); } if (goal == null) { return false; } if (goal.isInGoal(ctx.playerFeet())) { return false; } synchronized (pathPlanLock) { if (current != null) { return false; } synchronized (pathCalcLock) { if (inProgress != null) { return false; } queuePathEvent(PathEvent.CALC_STARTED); findPathInNewThread(expectedSegmentStart, true, context); return true; } } } @Override public Goal getGoal() { return goal; } @Override public boolean isPathing() { return hasPath() && !pausedThisTick; } @Override public PathExecutor getCurrent() { return current; } @Override public PathExecutor getNext() { return next; } @Override public Optional<AbstractNodeCostSearch> getInProgress() { return Optional.ofNullable(inProgress); } public boolean isSafeToCancel() { if (current == null) { return !baritone.getElytraProcess().isActive() || baritone.getElytraProcess().isSafeToCancel(); } return safeToCancel; } public void requestPause() { pauseRequestedLastTick = true; } public boolean cancelSegmentIfSafe() { if (isSafeToCancel()) { secretInternalSegmentCancel(); return true; } return false; } @Override public boolean cancelEverything() { boolean doIt = isSafeToCancel(); if (doIt) { secretInternalSegmentCancel(); } baritone.getPathingControlManager().cancelEverything(); // regardless of if we can stop the current segment, we can still stop the processes return doIt; } public boolean calcFailedLastTick() { // NOT exposed on public api return calcFailedLastTick; } public void softCancelIfSafe() { synchronized (pathPlanLock) { getInProgress().ifPresent(AbstractNodeCostSearch::cancel); // only cancel ours if (!isSafeToCancel()) { return; } current = null; next = null; } cancelRequested = true; // do everything BUT clear keys } // just cancel the current path public void secretInternalSegmentCancel() { queuePathEvent(PathEvent.CANCELED); synchronized (pathPlanLock) { getInProgress().ifPresent(AbstractNodeCostSearch::cancel); if (current != null) { current = null; next = null; baritone.getInputOverrideHandler().clearAllKeys(); baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); } } } @Override public void forceCancel() { // exposed on public api because :sob: cancelEverything(); secretInternalSegmentCancel(); synchronized (pathCalcLock) { inProgress = null; } } public CalculationContext secretInternalGetCalculationContext() { return context; } public Optional<Double> estimatedTicksToGoal() { BetterBlockPos currentPos = ctx.playerFeet(); if (goal == null || currentPos == null || startPosition == null) { return Optional.empty(); } if (goal.isInGoal(ctx.playerFeet())) { resetEstimatedTicksToGoal(); return Optional.of(0.0); } if (ticksElapsedSoFar == 0) { return Optional.empty(); } double current = goal.heuristic(currentPos.x, currentPos.y, currentPos.z); double start = goal.heuristic(startPosition.x, startPosition.y, startPosition.z); if (current == start) {// can't check above because current and start can be equal even if currentPos and startPosition are not return Optional.empty(); } double eta = Math.abs(current - goal.heuristic()) * ticksElapsedSoFar / Math.abs(start - current); return Optional.of(eta); } private void resetEstimatedTicksToGoal() { resetEstimatedTicksToGoal(expectedSegmentStart); } private void resetEstimatedTicksToGoal(BlockPos start) { resetEstimatedTicksToGoal(new BetterBlockPos(start)); } private void resetEstimatedTicksToGoal(BetterBlockPos start) { ticksElapsedSoFar = 0; startPosition = start; } /** * See issue #209 * * @return The starting {@link BlockPos} for a new path */ public BetterBlockPos pathStart() { // TODO move to a helper or util class BetterBlockPos feet = ctx.playerFeet(); if (!MovementHelper.canWalkOn(ctx, feet.below())) { if (ctx.player().isOnGround()) { double playerX = ctx.player().position().x; double playerZ = ctx.player().position().z; ArrayList<BetterBlockPos> closest = new ArrayList<>(); for (int dx = -1; dx <= 1; dx++) { for (int dz = -1; dz <= 1; dz++) { closest.add(new BetterBlockPos(feet.x + dx, feet.y, feet.z + dz)); } } closest.sort(Comparator.comparingDouble(pos -> ((pos.x + 0.5D) - playerX) * ((pos.x + 0.5D) - playerX) + ((pos.z + 0.5D) - playerZ) * ((pos.z + 0.5D) - playerZ))); for (int i = 0; i < 4; i++) { BetterBlockPos possibleSupport = closest.get(i); double xDist = Math.abs((possibleSupport.x + 0.5D) - playerX); double zDist = Math.abs((possibleSupport.z + 0.5D) - playerZ); if (xDist > 0.8 && zDist > 0.8) { // can't possibly be sneaking off of this one, we're too far away continue; } if (MovementHelper.canWalkOn(ctx, possibleSupport.below()) && MovementHelper.canWalkThrough(ctx, possibleSupport) && MovementHelper.canWalkThrough(ctx, possibleSupport.above())) { // this is plausible //logDebug("Faking path start assuming player is standing off the edge of a block"); return possibleSupport; } } } else { // !onGround // we're in the middle of a jump if (MovementHelper.canWalkOn(ctx, feet.below().below())) { //logDebug("Faking path start assuming player is midair and falling"); return feet.below(); } } } return feet; } /** * In a new thread, pathfind to target blockpos * * @param start * @param talkAboutIt */ private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt, CalculationContext context) { // this must be called with synchronization on pathCalcLock! // actually, we can check this, muahaha if (!Thread.holdsLock(pathCalcLock)) { throw new IllegalStateException("Must be called with synchronization on pathCalcLock"); // why do it this way? it's already indented so much that putting the whole thing in a synchronized(pathCalcLock) was just too much lol } if (inProgress != null) { throw new IllegalStateException("Already doing it"); // should have been checked by caller } if (!context.safeForThreadedUse) { throw new IllegalStateException("Improper context thread safety level"); } Goal goal = this.goal; if (goal == null) { logDebug("no goal"); // TODO should this be an exception too? definitely should be checked by caller return; } long primaryTimeout; long failureTimeout; if (current == null) { primaryTimeout = Baritone.settings().primaryTimeoutMS.value; failureTimeout = Baritone.settings().failureTimeoutMS.value; } else { primaryTimeout = Baritone.settings().planAheadPrimaryTimeoutMS.value; failureTimeout = Baritone.settings().planAheadFailureTimeoutMS.value; } AbstractNodeCostSearch pathfinder = createPathfinder(start, goal, current == null ? null : current.getPath(), context); if (!Objects.equals(pathfinder.getGoal(), goal)) { // will return the exact same object if simplification didn't happen logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); } inProgress = pathfinder; Baritone.getExecutor().execute(() -> { if (talkAboutIt) { logDebug("Starting to search for path from " + start + " to " + goal); } PathCalculationResult calcResult = pathfinder.calculate(primaryTimeout, failureTimeout); synchronized (pathPlanLock) { Optional<PathExecutor> executor = calcResult.getPath().map(p -> new PathExecutor(PathingBehavior.this, p)); if (current == null) { if (executor.isPresent()) { if (executor.get().getPath().positions().contains(expectedSegmentStart)) { queuePathEvent(PathEvent.CALC_FINISHED_NOW_EXECUTING); current = executor.get(); resetEstimatedTicksToGoal(start); } else { logDebug("Warning: discarding orphan path segment with incorrect start"); } } else { if (calcResult.getType() != PathCalculationResult.Type.CANCELLATION && calcResult.getType() != PathCalculationResult.Type.EXCEPTION) { // don't dispatch CALC_FAILED on cancellation queuePathEvent(PathEvent.CALC_FAILED); } } } else { if (next == null) { if (executor.isPresent()) { if (executor.get().getPath().getSrc().equals(current.getPath().getDest())) { queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_FINISHED); next = executor.get(); } else { logDebug("Warning: discarding orphan next segment with incorrect start"); } } else { queuePathEvent(PathEvent.NEXT_CALC_FAILED); } } else { //throw new IllegalStateException("I have no idea what to do with this path"); // no point in throwing an exception here, and it gets it stuck with inProgress being not null logDirect("Warning: PathingBehaivor illegal state! Discarding invalid path!"); } } if (talkAboutIt && current != null && current.getPath() != null) { if (goal.isInGoal(current.getPath().getDest())) { logDebug("Finished finding a path from " + start + " to " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered"); } else { logDebug("Found path segment from " + start + " towards " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered"); } } synchronized (pathCalcLock) { inProgress = null; } } }); } private AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) { Goal transformed = goal; if (Baritone.settings().simplifyUnloadedYCoord.value && goal instanceof IGoalRenderPos) { BlockPos pos = ((IGoalRenderPos) goal).getGoalPos(); if (!context.bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ())) { transformed = new GoalXZ(pos.getX(), pos.getZ()); } } Favoring favoring = new Favoring(context.getBaritone().getPlayerContext(), previous, context); BetterBlockPos feet = ctx.playerFeet(); var realStart = new BetterBlockPos(start); var sub = feet.subtract(realStart); if (feet.getY() == realStart.getY() && Math.abs(sub.getX()) <= 1 && Math.abs(sub.getZ()) <= 1) { realStart = feet; } return new AStarPathFinder(realStart, start.getX(), start.getY(), start.getZ(), transformed, favoring, context); } @Override public void onRenderPass(RenderEvent event) { PathRenderer.render(event, this); } } ================================================ FILE: src/main/java/baritone/behavior/WaypointBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.behavior; import baritone.Baritone; import baritone.api.cache.IWaypoint; import baritone.api.cache.Waypoint; import baritone.api.event.events.BlockInteractEvent; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; import baritone.utils.BlockStateInterface; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import net.minecraft.world.level.block.BedBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BedPart; import java.util.Set; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class WaypointBehavior extends Behavior { public WaypointBehavior(Baritone baritone) { super(baritone); } @Override public void onBlockInteract(BlockInteractEvent event) { if (!Baritone.settings().doBedWaypoints.value) return; if (event.getType() == BlockInteractEvent.Type.USE) { BetterBlockPos pos = BetterBlockPos.from(event.getPos()); BlockState state = BlockStateInterface.get(ctx, pos); if (state.getBlock() instanceof BedBlock) { if (state.getValue(BedBlock.PART) == BedPart.FOOT) { pos = pos.relative(state.getValue(BedBlock.FACING)); } Set<IWaypoint> waypoints = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getByTag(IWaypoint.Tag.BED); boolean exists = waypoints.stream().map(IWaypoint::getLocation).filter(pos::equals).findFirst().isPresent(); if (!exists) { baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, pos)); } } } } @Override public void onPlayerDeath() { if (!Baritone.settings().doDeathWaypoints.value) return; Waypoint deathWaypoint = new Waypoint("death", Waypoint.Tag.DEATH, ctx.playerFeet()); baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(deathWaypoint); MutableComponent component = Component.literal("Death position saved."); component.setStyle(component.getStyle() .withColor(ChatFormatting.WHITE) .withHoverEvent(new HoverEvent( HoverEvent.Action.SHOW_TEXT, Component.literal("Click to goto death") )) .withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format( "%s%s goto %s @ %d", FORCE_COMMAND_PREFIX, "wp", deathWaypoint.getTag().getName(), deathWaypoint.getCreationTimestamp() ) ))); Helper.HELPER.logDirect(component); } } ================================================ FILE: src/main/java/baritone/behavior/look/ForkableRandom.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.behavior.look; import java.util.Arrays; import java.util.concurrent.atomic.AtomicLong; import java.util.function.LongSupplier; /** * Implementation of Xoroshiro256++ * <p> * Extended to produce random double-precision floating point numbers, and allow copies to be spawned via {@link #fork}, * which share the same internal state as the source object. * * @author Brady */ public final class ForkableRandom { private static final double DOUBLE_UNIT = 0x1.0p-53; private final long[] s; public ForkableRandom() { this(System.nanoTime() ^ System.currentTimeMillis()); } public ForkableRandom(long seedIn) { final AtomicLong seed = new AtomicLong(seedIn); final LongSupplier splitmix64 = () -> { long z = seed.addAndGet(0x9e3779b97f4a7c15L); z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L; z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL; return z ^ (z >>> 31); }; this.s = new long[] { splitmix64.getAsLong(), splitmix64.getAsLong(), splitmix64.getAsLong(), splitmix64.getAsLong() }; } private ForkableRandom(long[] s) { this.s = s; } public double nextDouble() { return (this.next() >>> 11) * DOUBLE_UNIT; } public long next() { final long result = rotl(this.s[0] + this.s[3], 23) + this.s[0]; final long t = this.s[1] << 17; this.s[2] ^= this.s[0]; this.s[3] ^= this.s[1]; this.s[1] ^= this.s[2]; this.s[0] ^= this.s[3]; this.s[2] ^= t; this.s[3] = rotl(this.s[3], 45); return result; } public ForkableRandom fork() { return new ForkableRandom(Arrays.copyOf(this.s, 4)); } private static long rotl(long x, int k) { return (x << k) | (x >>> (64 - k)); } } ================================================ FILE: src/main/java/baritone/cache/CachedChunk.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.api.utils.BlockUtils; import baritone.utils.pathing.PathingBlockType; import com.google.common.collect.ImmutableSet; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.dimension.DimensionType; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import java.util.Map; /** * @author Brady * @since 8/3/2018 */ public final class CachedChunk { public static final ImmutableSet<Block> BLOCKS_TO_KEEP_TRACK_OF = ImmutableSet.of( Blocks.ENDER_CHEST, Blocks.FURNACE, Blocks.CHEST, Blocks.TRAPPED_CHEST, Blocks.END_PORTAL, Blocks.END_PORTAL_FRAME, Blocks.SPAWNER, Blocks.BARRIER, Blocks.OBSERVER, Blocks.WHITE_SHULKER_BOX, Blocks.ORANGE_SHULKER_BOX, Blocks.MAGENTA_SHULKER_BOX, Blocks.LIGHT_BLUE_SHULKER_BOX, Blocks.YELLOW_SHULKER_BOX, Blocks.LIME_SHULKER_BOX, Blocks.PINK_SHULKER_BOX, Blocks.GRAY_SHULKER_BOX, Blocks.LIGHT_GRAY_SHULKER_BOX, Blocks.CYAN_SHULKER_BOX, Blocks.PURPLE_SHULKER_BOX, Blocks.BLUE_SHULKER_BOX, Blocks.BROWN_SHULKER_BOX, Blocks.GREEN_SHULKER_BOX, Blocks.RED_SHULKER_BOX, Blocks.BLACK_SHULKER_BOX, Blocks.NETHER_PORTAL, Blocks.HOPPER, Blocks.BEACON, Blocks.BREWING_STAND, // TODO: Maybe add a predicate for blocks to keep track of? // This should really not need to happen Blocks.CREEPER_HEAD, Blocks.CREEPER_WALL_HEAD, Blocks.DRAGON_HEAD, Blocks.DRAGON_WALL_HEAD, Blocks.PLAYER_HEAD, Blocks.PLAYER_WALL_HEAD, Blocks.ZOMBIE_HEAD, Blocks.ZOMBIE_WALL_HEAD, Blocks.SKELETON_SKULL, Blocks.SKELETON_WALL_SKULL, Blocks.WITHER_SKELETON_SKULL, Blocks.WITHER_SKELETON_WALL_SKULL, Blocks.ENCHANTING_TABLE, Blocks.ANVIL, Blocks.WHITE_BED, Blocks.ORANGE_BED, Blocks.MAGENTA_BED, Blocks.LIGHT_BLUE_BED, Blocks.YELLOW_BED, Blocks.LIME_BED, Blocks.PINK_BED, Blocks.GRAY_BED, Blocks.LIGHT_GRAY_BED, Blocks.CYAN_BED, Blocks.PURPLE_BED, Blocks.BLUE_BED, Blocks.BROWN_BED, Blocks.GREEN_BED, Blocks.RED_BED, Blocks.BLACK_BED, Blocks.DRAGON_EGG, Blocks.JUKEBOX, Blocks.END_GATEWAY, Blocks.COBWEB, Blocks.NETHER_WART, Blocks.LADDER, Blocks.VINE ); public final int height; /** * The size of the chunk data in bits. Equal to 16 KiB. * <p> * Chunks are 16x16xH, each block requires 2 bits. */ public final int size; /** * The size of the chunk data in bytes. Equal to 16 KiB for 256 height. */ public final int sizeInBytes; /** * The chunk x coordinate */ public final int x; /** * The chunk z coordinate */ public final int z; /** * The actual raw data of this packed chunk. * <p> * Each block is expressed as 2 bits giving a total of 16 KiB */ private final BitSet data; private final Int2ObjectOpenHashMap<String> special; /** * The block names of each surface level block for generating an overview */ private final BlockState[] overview; private final int[] heightMap; private final Map<String, List<BlockPos>> specialBlockLocations; public final long cacheTimestamp; CachedChunk(int x, int z, int height, BitSet data, BlockState[] overview, Map<String, List<BlockPos>> specialBlockLocations, long cacheTimestamp) { this.size = size(height); this.sizeInBytes = sizeInBytes(size); validateSize(data); this.x = x; this.z = z; this.height = height; this.data = data; this.overview = overview; this.heightMap = new int[256]; this.specialBlockLocations = specialBlockLocations; this.cacheTimestamp = cacheTimestamp; if (specialBlockLocations.isEmpty()) { this.special = null; } else { this.special = new Int2ObjectOpenHashMap<>(); setSpecial(); } calculateHeightMap(); } public static int size(int dimension_height) { return 2 * 16 * 16 * dimension_height; } public static int sizeInBytes(int size) { return size / 8; } private final void setSpecial() { for (Map.Entry<String, List<BlockPos>> entry : specialBlockLocations.entrySet()) { for (BlockPos pos : entry.getValue()) { special.put(getPositionIndex(pos.getX(), pos.getY(), pos.getZ()), entry.getKey()); } } } public final BlockState getBlock(int x, int y, int z, DimensionType dimension) { int index = getPositionIndex(x, y, z); PathingBlockType type = getType(index); int internalPos = z << 4 | x; if (heightMap[internalPos] == y && type != PathingBlockType.AVOID) { // if the top block in a column is water, we cache it as AVOID but we don't want to just return default state water (which is not flowing) beacuse then it would try to path through it // we have this exact block, it's a surface block /*System.out.println("Saying that " + x + "," + y + "," + z + " is " + state); if (!Minecraft.getInstance().world.getBlockState(new BlockPos(x + this.x * 16, y, z + this.z * 16)).getBlock().equals(state.getBlock())) { throw new IllegalStateException("failed " + Minecraft.getInstance().world.getBlockState(new BlockPos(x + this.x * 16, y, z + this.z * 16)).getBlock() + " " + state.getBlock() + " " + (x + this.x * 16) + " " + y + " " + (z + this.z * 16)); }*/ return overview[internalPos]; } if (special != null) { String str = special.get(index); if (str != null) { return BlockUtils.stringToBlockRequired(str).defaultBlockState(); } } if (type == PathingBlockType.SOLID) { if (y == dimension.logicalHeight() - 1 && dimension.hasCeiling()) { // nether roof is always unbreakable return Blocks.BEDROCK.defaultBlockState(); } if (y < -59 && dimension.natural()) { // solid blocks below 5 are commonly bedrock // however, returning bedrock always would be a little yikes // discourage paths that include breaking blocks below 5 a little more heavily just so that it takes paths breaking what's known to be stone (at 5 or above) instead of what could maybe be bedrock (below 5) return Blocks.OBSIDIAN.defaultBlockState(); } } return ChunkPacker.pathingTypeToBlock(type, dimension); } private PathingBlockType getType(int index) { return PathingBlockType.fromBits(data.get(index), data.get(index + 1)); } private void calculateHeightMap() { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { int index = z << 4 | x; heightMap[index] = 0; for (int y = height; y >= 0; y--) { int i = getPositionIndex(x, y, z); if (data.get(i) || data.get(i + 1)) { heightMap[index] = y; break; } } } } } public final BlockState[] getOverview() { return overview; } public final Map<String, List<BlockPos>> getRelativeBlocks() { return specialBlockLocations; } public final ArrayList<BlockPos> getAbsoluteBlocks(String blockType) { if (specialBlockLocations.get(blockType) == null) { return null; } ArrayList<BlockPos> res = new ArrayList<>(); for (BlockPos pos : specialBlockLocations.get(blockType)) { res.add(new BlockPos(pos.getX() + x * 16, pos.getY(), pos.getZ() + z * 16)); } return res; } /** * @return Returns the raw packed chunk data as a byte array */ public final byte[] toByteArray() { return this.data.toByteArray(); } /** * Returns the raw bit index of the specified position * * @param x The x position * @param y The y position * @param z The z position * @return The bit index */ public static int getPositionIndex(int x, int y, int z) { return (x << 1) | (z << 5) | (y << 9); } /** * Validates the size of an input {@link BitSet} containing the raw * packed chunk data. Sizes that exceed {@link CachedChunk#size} are * considered invalid, and thus, an exception will be thrown. * * @param data The raw data * @throws IllegalArgumentException if the bitset size exceeds the maximum size */ private void validateSize(BitSet data) { if (data.size() > size) { throw new IllegalArgumentException("BitSet of invalid length provided"); } } } ================================================ FILE: src/main/java/baritone/cache/CachedRegion.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.Baritone; import baritone.api.cache.ICachedRegion; import baritone.api.utils.BlockUtils; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.dimension.DimensionType; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; /** * @author Brady * @since 8/3/2018 */ public final class CachedRegion implements ICachedRegion { private static final byte CHUNK_NOT_PRESENT = 0; private static final byte CHUNK_PRESENT = 1; /** * Magic value to detect invalid cache files, or incompatible cache files saved in an old version of Baritone */ private static final int CACHED_REGION_MAGIC = 456022911; /** * All of the chunks in this region: A 32x32 array of them. */ private final CachedChunk[][] chunks = new CachedChunk[32][32]; /** * The region x coordinate */ private final int x; /** * The region z coordinate */ private final int z; private final DimensionType dimension; /** * Has this region been modified since its most recent load or save */ private boolean hasUnsavedChanges; CachedRegion(int x, int z, DimensionType dimension) { this.x = x; this.z = z; this.hasUnsavedChanges = false; this.dimension = dimension; } @Override public final BlockState getBlock(int x, int y, int z) { int adjY = y - dimension.minY(); CachedChunk chunk = chunks[x >> 4][z >> 4]; if (chunk != null) { return chunk.getBlock(x & 15, adjY, z & 15, dimension); } return null; } @Override public final boolean isCached(int x, int z) { return chunks[x >> 4][z >> 4] != null; } public final ArrayList<BlockPos> getLocationsOf(String block) { ArrayList<BlockPos> res = new ArrayList<>(); for (int chunkX = 0; chunkX < 32; chunkX++) { for (int chunkZ = 0; chunkZ < 32; chunkZ++) { if (chunks[chunkX][chunkZ] == null) { continue; } ArrayList<BlockPos> locs = chunks[chunkX][chunkZ].getAbsoluteBlocks(block); if (locs != null) { res.addAll(locs); } } } return res; } public final synchronized void updateCachedChunk(int chunkX, int chunkZ, CachedChunk chunk) { this.chunks[chunkX][chunkZ] = chunk; hasUnsavedChanges = true; } public synchronized final void save(String directory) { if (!hasUnsavedChanges) { return; } removeExpired(); try { Path path = Paths.get(directory); if (!Files.exists(path)) { Files.createDirectories(path); } System.out.println("Saving region " + x + "," + z + " to disk " + path); Path regionFile = getRegionFile(path, this.x, this.z); if (!Files.exists(regionFile)) { Files.createFile(regionFile); } try ( FileOutputStream fileOut = new FileOutputStream(regionFile.toFile()); GZIPOutputStream gzipOut = new GZIPOutputStream(fileOut, 16384); DataOutputStream out = new DataOutputStream(gzipOut) ) { out.writeInt(CACHED_REGION_MAGIC); for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { CachedChunk chunk = this.chunks[x][z]; if (chunk == null) { out.write(CHUNK_NOT_PRESENT); } else { out.write(CHUNK_PRESENT); byte[] chunkBytes = chunk.toByteArray(); out.write(chunkBytes); // Messy, but fills the empty 0s that should be trailing to fill up the space. out.write(new byte[chunk.sizeInBytes - chunkBytes.length]); } } } for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (chunks[x][z] != null) { for (int i = 0; i < 256; i++) { out.writeUTF(BlockUtils.blockToString(chunks[x][z].getOverview()[i].getBlock())); } } } } for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (chunks[x][z] != null) { Map<String, List<BlockPos>> locs = chunks[x][z].getRelativeBlocks(); out.writeShort(locs.entrySet().size()); for (Map.Entry<String, List<BlockPos>> entry : locs.entrySet()) { out.writeUTF(entry.getKey()); out.writeShort(entry.getValue().size()); for (BlockPos pos : entry.getValue()) { out.writeByte((byte) (pos.getZ() << 4 | pos.getX())); out.writeInt(pos.getY()-dimension.minY()); } } } } } for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (chunks[x][z] != null) { out.writeLong(chunks[x][z].cacheTimestamp); } } } } hasUnsavedChanges = false; System.out.println("Saved region successfully"); } catch (Exception ex) { ex.printStackTrace(); } } public synchronized void load(String directory) { try { Path path = Paths.get(directory); if (!Files.exists(path)) { Files.createDirectories(path); } Path regionFile = getRegionFile(path, this.x, this.z); if (!Files.exists(regionFile)) { return; } System.out.println("Loading region " + x + "," + z + " from disk " + path); long start = System.nanoTime() / 1000000L; try ( FileInputStream fileIn = new FileInputStream(regionFile.toFile()); GZIPInputStream gzipIn = new GZIPInputStream(fileIn, 32768); DataInputStream in = new DataInputStream(gzipIn) ) { int magic = in.readInt(); if (magic != CACHED_REGION_MAGIC) { // in the future, if we change the format on disk // we can keep converters for the old format // by switching on the magic value, and either loading it normally, or loading through a converter. throw new IOException("Bad magic value " + magic); } boolean[][] present = new boolean[32][32]; BitSet[][] bitSets = new BitSet[32][32]; Map<String, List<BlockPos>>[][] location = new Map[32][32]; BlockState[][][] overview = new BlockState[32][32][]; long[][] cacheTimestamp = new long[32][32]; for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { int isChunkPresent = in.read(); switch (isChunkPresent) { case CHUNK_PRESENT: byte[] bytes = new byte[CachedChunk.sizeInBytes(CachedChunk.size(dimension.height()))]; in.readFully(bytes); bitSets[x][z] = BitSet.valueOf(bytes); location[x][z] = new HashMap<>(); //this is top block in columns overview[x][z] = new BlockState[256]; present[x][z] = true; break; case CHUNK_NOT_PRESENT: break; default: throw new IOException("Malformed stream"); } } } for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (present[x][z]) { for (int i = 0; i < 256; i++) { overview[x][z][i] = BlockUtils.stringToBlockRequired(in.readUTF()).defaultBlockState(); } } } } for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (present[x][z]) { // 16 * 16 * 256 = 65536 so a short is enough // ^ haha jokes on leijurv, java doesn't have unsigned types so that isn't correct // also why would you have more than 32767 special blocks in a chunk // haha double jokes on you now it works for 65535 not just 32767 int numSpecialBlockTypes = in.readShort() & 0xffff; for (int i = 0; i < numSpecialBlockTypes; i++) { String blockName = in.readUTF(); BlockUtils.stringToBlockRequired(blockName); List<BlockPos> locs = new ArrayList<>(); location[x][z].put(blockName, locs); int numLocations = in.readShort() & 0xffff; if (numLocations == 0) { // an entire chunk full of air can happen in the end numLocations = 65536; } for (int j = 0; j < numLocations; j++) { byte xz = in.readByte(); int X = xz & 0x0f; int Z = (xz >>> 4) & 0x0f; int Y = in.readInt(); locs.add(new BlockPos(X, Y+dimension.minY(), Z)); } } } } } for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (present[x][z]) { cacheTimestamp[x][z] = in.readLong(); } } } // only if the entire file was uncorrupted do we actually set the chunks for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (present[x][z]) { int regionX = this.x; int regionZ = this.z; int chunkX = x + 32 * regionX; int chunkZ = z + 32 * regionZ; this.chunks[x][z] = new CachedChunk(chunkX, chunkZ, dimension.height(), bitSets[x][z], overview[x][z], location[x][z], cacheTimestamp[x][z]); } } } } removeExpired(); hasUnsavedChanges = false; long end = System.nanoTime() / 1000000L; System.out.println("Loaded region successfully in " + (end - start) + "ms"); } catch (Exception ex) { // corrupted files can cause NullPointerExceptions as well as IOExceptions ex.printStackTrace(); } } public synchronized final void removeExpired() { long expiry = Baritone.settings().cachedChunksExpirySeconds.value; if (expiry < 0) { return; } long now = System.currentTimeMillis(); long oldestAcceptableAge = now - expiry * 1000L; for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (this.chunks[x][z] != null && this.chunks[x][z].cacheTimestamp < oldestAcceptableAge) { System.out.println("Removing chunk " + (x + 32 * this.x) + "," + (z + 32 * this.z) + " because it was cached " + (now - this.chunks[x][z].cacheTimestamp) / 1000L + " seconds ago, and max age is " + expiry); this.chunks[x][z] = null; } } } } public synchronized final CachedChunk mostRecentlyModified() { CachedChunk recent = null; for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { if (this.chunks[x][z] == null) { continue; } if (recent == null || this.chunks[x][z].cacheTimestamp > recent.cacheTimestamp) { recent = this.chunks[x][z]; } } } return recent; } /** * @return The region x coordinate */ @Override public final int getX() { return this.x; } /** * @return The region z coordinate */ @Override public final int getZ() { return this.z; } private static Path getRegionFile(Path cacheDir, int regionX, int regionZ) { return Paths.get(cacheDir.toString(), "r." + regionX + "." + regionZ + ".bcr"); } } ================================================ FILE: src/main/java/baritone/cache/CachedWorld.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWorldData; import baritone.api.utils.Helper; import com.google.common.cache.CacheBuilder; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.dimension.DimensionType; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; /** * @author Brady * @since 8/4/2018 */ public final class CachedWorld implements ICachedWorld, Helper { /** * The maximum number of regions in any direction from (0,0) */ private static final int REGION_MAX = 30_000_000 / 512 + 1; /** * A map of all of the cached regions. */ private Long2ObjectMap<CachedRegion> cachedRegions = new Long2ObjectOpenHashMap<>(); /** * The directory that the cached region files are saved to */ private final String directory; /** * Queue of positions to pack. Refers to the toPackMap, in that every element of this queue will be a * key in that map. */ private final LinkedBlockingQueue<ChunkPos> toPackQueue = new LinkedBlockingQueue<>(); /** * All chunk positions pending packing. This map will be updated in-place if a new update to the chunk occurs * while waiting in the queue for the packer thread to get to it. */ private final Map<ChunkPos, LevelChunk> toPackMap = CacheBuilder.newBuilder().softValues().<ChunkPos, LevelChunk>build().asMap(); private final DimensionType dimension; CachedWorld(Path directory, DimensionType dimension) { if (!Files.exists(directory)) { try { Files.createDirectories(directory); } catch (IOException ignored) { } } this.directory = directory.toString(); this.dimension = dimension; System.out.println("Cached world directory: " + directory); Baritone.getExecutor().execute(new PackerThread()); Baritone.getExecutor().execute(() -> { try { Thread.sleep(30000); while (true) { // since a region only saves if it's been modified since its last save // saving every 10 minutes means that once it's time to exit // we'll only have a couple regions to save save(); Thread.sleep(600000); } } catch (InterruptedException e) { e.printStackTrace(); } }); } @Override public final void queueForPacking(LevelChunk chunk) { if (toPackMap.put(chunk.getPos(), chunk) == null) { toPackQueue.add(chunk.getPos()); } } @Override public final boolean isCached(int blockX, int blockZ) { CachedRegion region = getRegion(blockX >> 9, blockZ >> 9); if (region == null) { return false; } return region.isCached(blockX & 511, blockZ & 511); } public final boolean regionLoaded(int blockX, int blockZ) { return getRegion(blockX >> 9, blockZ >> 9) != null; } @Override public final ArrayList<BlockPos> getLocationsOf(String block, int maximum, int centerX, int centerZ, int maxRegionDistanceSq) { ArrayList<BlockPos> res = new ArrayList<>(); int centerRegionX = centerX >> 9; int centerRegionZ = centerZ >> 9; int searchRadius = 0; while (searchRadius <= maxRegionDistanceSq) { for (int xoff = -searchRadius; xoff <= searchRadius; xoff++) { for (int zoff = -searchRadius; zoff <= searchRadius; zoff++) { int distance = xoff * xoff + zoff * zoff; if (distance != searchRadius) { continue; } int regionX = xoff + centerRegionX; int regionZ = zoff + centerRegionZ; CachedRegion region = getOrCreateRegion(regionX, regionZ); if (region != null) { // TODO: 100% verify if this or addAll is faster. res.addAll(region.getLocationsOf(block)); } } } if (res.size() >= maximum) { return res; } searchRadius++; } return res; } private void updateCachedChunk(CachedChunk chunk) { CachedRegion region = getOrCreateRegion(chunk.x >> 5, chunk.z >> 5); region.updateCachedChunk(chunk.x & 31, chunk.z & 31, chunk); } @Override public final void save() { if (!Baritone.settings().chunkCaching.value) { System.out.println("Not saving to disk; chunk caching is disabled."); allRegions().forEach(region -> { if (region != null) { region.removeExpired(); } }); // even if we aren't saving to disk, still delete expired old chunks from RAM prune(); return; } long start = System.nanoTime() / 1000000L; allRegions().parallelStream().forEach(region -> { if (region != null) { region.save(this.directory); } }); long now = System.nanoTime() / 1000000L; System.out.println("World save took " + (now - start) + "ms"); prune(); } /** * Delete regions that are too far from the player */ private synchronized void prune() { if (!Baritone.settings().pruneRegionsFromRAM.value) { return; } BlockPos pruneCenter = guessPosition(); for (CachedRegion region : allRegions()) { if (region == null) { continue; } int distX = ((region.getX() << 9) + 256) - pruneCenter.getX(); int distZ = ((region.getZ() << 9) + 256) - pruneCenter.getZ(); double dist = Math.sqrt(distX * distX + distZ * distZ); if (dist > 1024) { logDebug("Deleting cached region from ram"); cachedRegions.remove(getRegionID(region.getX(), region.getZ())); } } } /** * If we are still in this world and dimension, return player feet, otherwise return most recently modified chunk */ private BlockPos guessPosition() { for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { IWorldData data = ibaritone.getWorldProvider().getCurrentWorld(); if (data != null && data.getCachedWorld() == this && ibaritone.getPlayerContext().player() != null) { return ibaritone.getPlayerContext().playerFeet(); } } CachedChunk mostRecentlyModified = null; for (CachedRegion region : allRegions()) { if (region == null) { continue; } CachedChunk ch = region.mostRecentlyModified(); if (ch == null) { continue; } if (mostRecentlyModified == null || mostRecentlyModified.cacheTimestamp < ch.cacheTimestamp) { mostRecentlyModified = ch; } } if (mostRecentlyModified == null) { return new BlockPos(0, 0, 0); } return new BlockPos((mostRecentlyModified.x << 4) + 8, 0, (mostRecentlyModified.z << 4) + 8); } private synchronized List<CachedRegion> allRegions() { return new ArrayList<>(this.cachedRegions.values()); } @Override public final void reloadAllFromDisk() { long start = System.nanoTime() / 1000000L; allRegions().forEach(region -> { if (region != null) { region.load(this.directory); } }); long now = System.nanoTime() / 1000000L; System.out.println("World load took " + (now - start) + "ms"); } @Override public final synchronized CachedRegion getRegion(int regionX, int regionZ) { return cachedRegions.get(getRegionID(regionX, regionZ)); } /** * Returns the region at the specified region coordinates. If a * region is not found, then a new one is created. * * @param regionX The region X coordinate * @param regionZ The region Z coordinate * @return The region located at the specified coordinates */ private synchronized CachedRegion getOrCreateRegion(int regionX, int regionZ) { return cachedRegions.computeIfAbsent(getRegionID(regionX, regionZ), id -> { CachedRegion newRegion = new CachedRegion(regionX, regionZ, dimension); newRegion.load(this.directory); return newRegion; }); } public void tryLoadFromDisk(int regionX, int regionZ) { getOrCreateRegion(regionX, regionZ); } /** * Returns the region ID based on the region coordinates. 0 will be * returned if the specified region coordinates are out of bounds. * * @param regionX The region X coordinate * @param regionZ The region Z coordinate * @return The region ID */ private long getRegionID(int regionX, int regionZ) { if (!isRegionInWorld(regionX, regionZ)) { return 0; } return (long) regionX & 0xFFFFFFFFL | ((long) regionZ & 0xFFFFFFFFL) << 32; } /** * Returns whether or not the specified region coordinates is within the world bounds. * * @param regionX The region X coordinate * @param regionZ The region Z coordinate * @return Whether or not the region is in world bounds */ private boolean isRegionInWorld(int regionX, int regionZ) { return regionX <= REGION_MAX && regionX >= -REGION_MAX && regionZ <= REGION_MAX && regionZ >= -REGION_MAX; } private class PackerThread implements Runnable { public void run() { while (true) { try { ChunkPos pos = toPackQueue.take(); LevelChunk chunk = toPackMap.remove(pos); if (toPackQueue.size() > Baritone.settings().chunkPackerQueueMaxSize.value) { continue; } CachedChunk cached = ChunkPacker.pack(chunk); CachedWorld.this.updateCachedChunk(cached); //System.out.println("Processed chunk at " + chunk.x + "," + chunk.z); } catch (InterruptedException e) { e.printStackTrace(); break; } catch (Throwable th) { // in the case of an exception, keep consuming from the queue so as not to leak memory th.printStackTrace(); } } } } } ================================================ FILE: src/main/java/baritone/cache/ChunkPacker.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.api.utils.BlockUtils; import baritone.pathing.movement.MovementHelper; import baritone.utils.pathing.PathingBlockType; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.DoublePlantBlock; import net.minecraft.world.level.block.FlowerBlock; import net.minecraft.world.level.block.TallGrassBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.phys.Vec3; import java.util.*; import static baritone.utils.BlockStateInterface.getFromChunk; /** * @author Brady * @since 8/3/2018 */ public final class ChunkPacker { private ChunkPacker() {} public static CachedChunk pack(LevelChunk chunk) { //long start = System.nanoTime() / 1000000L; Map<String, List<BlockPos>> specialBlocks = new HashMap<>(); final int height = chunk.getLevel().dimensionType().height(); BitSet bitSet = new BitSet(CachedChunk.size(height)); try { LevelChunkSection[] chunkInternalStorageArray = chunk.getSections(); for (int y0 = 0; y0 < height / 16; y0++) { LevelChunkSection extendedblockstorage = chunkInternalStorageArray[y0]; if (extendedblockstorage == null) { // any 16x16x16 area that's all air will have null storage // for example, in an ocean biome, with air from y=64 to y=256 // the first 4 extended blocks storages will be full // and the remaining 12 will be null // since the index into the bitset is calculated from the x y and z // and doesn't function as an append, we can entirely skip the scanning // since a bitset is initialized to all zero, and air is saved as zeros continue; } PalettedContainer<BlockState> bsc = extendedblockstorage.getStates(); int yReal = y0 << 4; // the mapping of BlockStateContainer.getIndex from xyz to index is y << 8 | z << 4 | x; // for better cache locality, iterate in that order for (int y1 = 0; y1 < 16; y1++) { int y = y1 | yReal; for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { int index = CachedChunk.getPositionIndex(x, y, z); BlockState state = bsc.get(x, y1, z); boolean[] bits = getPathingBlockType(state, chunk, x, y + chunk.getMinBuildHeight(), z).getBits(); bitSet.set(index, bits[0]); bitSet.set(index + 1, bits[1]); Block block = state.getBlock(); if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(block)) { String name = BlockUtils.blockToString(block); specialBlocks.computeIfAbsent(name, b -> new ArrayList<>()).add(new BlockPos(x, y+chunk.getMinBuildHeight(), z)); } } } } } } catch (Exception e) { e.printStackTrace(); } //long end = System.nanoTime() / 1000000L; //System.out.println("Chunk packing took " + (end - start) + "ms for " + chunk.x + "," + chunk.z); BlockState[] blocks = new BlockState[256]; // get top block in columns // @formatter:off for (int z = 0; z < 16; z++) { https://www.ibm.com/developerworks/library/j-perry-writing-good-java-code/index.html for (int x = 0; x < 16; x++) { for (int y = height - 1; y >= 0; y--) { int index = CachedChunk.getPositionIndex(x, y, z); if (bitSet.get(index) || bitSet.get(index + 1)) { blocks[z << 4 | x] = getFromChunk(chunk, x, y, z); continue https; } } blocks[z << 4 | x] = Blocks.AIR.defaultBlockState(); } } // @formatter:on return new CachedChunk(chunk.getPos().x, chunk.getPos().z, height, bitSet, blocks, specialBlocks, System.currentTimeMillis()); } private static PathingBlockType getPathingBlockType(BlockState state, LevelChunk chunk, int x, int y, int z) { Block block = state.getBlock(); if (MovementHelper.isWater(state)) { // only water source blocks are plausibly usable, flowing water should be avoid // FLOWING_WATER is a waterfall, it doesn't really matter and caching it as AVOID just makes it look wrong if (MovementHelper.possiblyFlowing(state)) { return PathingBlockType.AVOID; } int adjY = y - chunk.getLevel().dimensionType().minY(); if ( (x != 15 && MovementHelper.possiblyFlowing(getFromChunk(chunk, x + 1, adjY, z))) || (x != 0 && MovementHelper.possiblyFlowing(getFromChunk(chunk, x - 1, adjY, z))) || (z != 15 && MovementHelper.possiblyFlowing(getFromChunk(chunk, x, adjY, z + 1))) || (z != 0 && MovementHelper.possiblyFlowing(getFromChunk(chunk, x, adjY, z - 1))) ) { return PathingBlockType.AVOID; } if (x == 0 || x == 15 || z == 0 || z == 15) { Vec3 flow = state.getFluidState().getFlow(chunk.getLevel(), new BlockPos(x + (chunk.getPos().x << 4), y, z + (chunk.getPos().z << 4))); if (flow.x != 0.0 || flow.z != 0.0) { return PathingBlockType.WATER; } return PathingBlockType.AVOID; } return PathingBlockType.WATER; } if (MovementHelper.avoidWalkingInto(state) || MovementHelper.isBottomSlab(state)) { return PathingBlockType.AVOID; } // We used to do an AABB check here // however, this failed in the nether when you were near a nether fortress // because fences check their adjacent blocks in the world for their fence connection status to determine AABB shape // this caused a nullpointerexception when we saved chunks on unload, because they were unable to check their neighbors if (block instanceof AirBlock || block instanceof TallGrassBlock || block instanceof DoublePlantBlock || block instanceof FlowerBlock) { return PathingBlockType.AIR; } return PathingBlockType.SOLID; } public static BlockState pathingTypeToBlock(PathingBlockType type, DimensionType dimension) { switch (type) { case AIR: return Blocks.AIR.defaultBlockState(); case WATER: return Blocks.WATER.defaultBlockState(); case AVOID: return Blocks.LAVA.defaultBlockState(); case SOLID: // Dimension solid types if (dimension.natural()) { return Blocks.STONE.defaultBlockState(); } if (dimension.ultraWarm()) { return Blocks.NETHERRACK.defaultBlockState(); } if (dimension.effectsLocation().equals(BuiltinDimensionTypes.END_EFFECTS)) { return Blocks.END_STONE.defaultBlockState(); } default: return null; } } } ================================================ FILE: src/main/java/baritone/cache/FasterWorldScanner.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWorldScanner; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BlockOptionalMeta; import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; import baritone.utils.accessor.IPalettedContainer; import io.netty.buffer.Unpooled; import net.minecraft.core.BlockPos; import net.minecraft.core.IdMapper; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.util.BitStorage; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.GlobalPalette; import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.SingleValuePalette; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public enum FasterWorldScanner implements IWorldScanner { INSTANCE; private static final BlockState[] PALETTE_REGISTRY_SENTINEL = new BlockState[0]; @Override public List<BlockPos> scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius) { assert ctx.world() != null; if (maxSearchRadius < 0) { throw new IllegalArgumentException("chunkRange must be >= 0"); } return scanChunksInternal(ctx, filter, getChunkRange(ctx.playerFeet().x >> 4, ctx.playerFeet().z >> 4, maxSearchRadius), max); } @Override public List<BlockPos> scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold) { Stream<BlockPos> stream = scanChunkInternal(ctx, filter, pos); if (max >= 0) { stream = stream.limit(max); } return stream.collect(Collectors.toList()); } @Override public int repack(IPlayerContext ctx) { return this.repack(ctx, 40); } @Override public int repack(IPlayerContext ctx, int range) { ChunkSource chunkProvider = ctx.world().getChunkSource(); ICachedWorld cachedWorld = ctx.worldData().getCachedWorld(); BetterBlockPos playerPos = ctx.playerFeet(); int playerChunkX = playerPos.getX() >> 4; int playerChunkZ = playerPos.getZ() >> 4; int minX = playerChunkX - range; int minZ = playerChunkZ - range; int maxX = playerChunkX + range; int maxZ = playerChunkZ + range; int queued = 0; for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { LevelChunk chunk = chunkProvider.getChunk(x, z, false); if (chunk != null && !chunk.isEmpty()) { queued++; cachedWorld.queueForPacking(chunk); } } } return queued; } // ordered in a way that the closest blocks are generally first public static List<ChunkPos> getChunkRange(int centerX, int centerZ, int chunkRadius) { List<ChunkPos> chunks = new ArrayList<>(); // spiral out chunks.add(new ChunkPos(centerX, centerZ)); for (int i = 1; i < chunkRadius; i++) { for (int j = 0; j <= i; j++) { chunks.add(new ChunkPos(centerX - j, centerZ - i)); if (j != 0) { chunks.add(new ChunkPos(centerX + j, centerZ - i)); chunks.add(new ChunkPos(centerX - j, centerZ + i)); } chunks.add(new ChunkPos(centerX + j, centerZ + i)); if (j != i) { chunks.add(new ChunkPos(centerX - i, centerZ - j)); chunks.add(new ChunkPos(centerX + i, centerZ - j)); if (j != 0) { chunks.add(new ChunkPos(centerX - i, centerZ + j)); chunks.add(new ChunkPos(centerX + i, centerZ + j)); } } } } return chunks; } private List<BlockPos> scanChunksInternal(IPlayerContext ctx, BlockOptionalMetaLookup lookup, List<ChunkPos> chunkPositions, int maxBlocks) { assert ctx.world() != null; try { // p -> scanChunkInternal(ctx, lookup, p) Stream<BlockPos> posStream = chunkPositions.parallelStream().flatMap(p -> scanChunkInternal(ctx, lookup, p)); if (maxBlocks >= 0) { // WARNING: this can be expensive if maxBlocks is large... // see limit's javadoc posStream = posStream.limit(maxBlocks); } return posStream.collect(Collectors.toList()); } catch (Exception e) { e.printStackTrace(); throw e; } } private Stream<BlockPos> scanChunkInternal(IPlayerContext ctx, BlockOptionalMetaLookup lookup, ChunkPos pos) { ChunkSource chunkProvider = ctx.world().getChunkSource(); // if chunk is not loaded, return empty stream if (!chunkProvider.hasChunk(pos.x, pos.z)) { return Stream.empty(); } long chunkX = (long) pos.x << 4; long chunkZ = (long) pos.z << 4; int playerSectionY = (ctx.playerFeet().y - ctx.world().getMinBuildHeight()) >> 4; return collectChunkSections(lookup, chunkProvider.getChunk(pos.x, pos.z, false), chunkX, chunkZ, playerSectionY).stream(); } private List<BlockPos> collectChunkSections(BlockOptionalMetaLookup lookup, LevelChunk chunk, long chunkX, long chunkZ, int playerSection) { // iterate over sections relative to player List<BlockPos> blocks = new ArrayList<>(); LevelChunkSection[] sections = chunk.getSections(); int l = sections.length; int i = playerSection - 1; int j = playerSection; for (; i >= 0 || j < l; ++j, --i) { if (j < l) { visitSection(lookup, sections[j], blocks, chunkX, chunkZ); } if (i >= 0) { visitSection(lookup, sections[i], blocks, chunkX, chunkZ); } } return blocks; } private void visitSection(BlockOptionalMetaLookup lookup, LevelChunkSection section, List<BlockPos> blocks, long chunkX, long chunkZ) { if (section == null || section.hasOnlyAir()) { return; } PalettedContainer<BlockState> sectionContainer = section.getStates(); //this won't work if the PaletteStorage is of the type EmptyPaletteStorage if (((IPalettedContainer<BlockState>) sectionContainer).getStorage() == null) { return; } int yOffset = section.bottomBlockY(); Palette<BlockState> palette = ((IPalettedContainer<BlockState>) sectionContainer).getPalette(); if (palette instanceof SingleValuePalette) { // single value palette doesn't have any data if (lookup.has(palette.valueFor(0))) { // TODO this is 4k hits, maybe don't return all of them? for (int x = 0; x < 16; ++x) { for (int y = 0; y < 16; ++y) { for (int z = 0; z < 16; ++z) { blocks.add(new BlockPos( (int) chunkX + x, yOffset + y, (int) chunkZ + z )); } } } } return; } boolean[] isInFilter = getIncludedFilterIndices(lookup, palette); if (isInFilter.length == 0) { return; } BitStorage array = ((IPalettedContainer<BlockState>) section.getStates()).getStorage(); long[] longArray = array.getRaw(); int arraySize = array.getSize(); int bitsPerEntry = array.getBits(); long maxEntryValue = (1L << bitsPerEntry) - 1L; for (int i = 0, idx = 0; i < longArray.length && idx < arraySize; ++i) { long l = longArray[i]; for (int offset = 0; offset <= (64 - bitsPerEntry) && idx < arraySize; offset += bitsPerEntry, ++idx) { int value = (int) ((l >> offset) & maxEntryValue); if (isInFilter[value]) { //noinspection DuplicateExpressions blocks.add(new BlockPos( (int) chunkX + ((idx & 255) & 15), yOffset + (idx >> 8), (int) chunkZ + ((idx & 255) >> 4) )); } } } } private boolean[] getIncludedFilterIndices(BlockOptionalMetaLookup lookup, Palette<BlockState> palette) { boolean commonBlockFound = false; BlockState[] paletteMap = getPalette(palette); if (paletteMap == PALETTE_REGISTRY_SENTINEL) { return getIncludedFilterIndicesFromRegistry(lookup); } int size = paletteMap.length; boolean[] isInFilter = new boolean[size]; for (int i = 0; i < size; i++) { BlockState state = paletteMap[i]; if (lookup.has(state)) { isInFilter[i] = true; commonBlockFound = true; } else { isInFilter[i] = false; } } if (!commonBlockFound) { return new boolean[0]; } return isInFilter; } private boolean[] getIncludedFilterIndicesFromRegistry(BlockOptionalMetaLookup lookup) { boolean[] isInFilter = new boolean[Block.BLOCK_STATE_REGISTRY.size()]; for (BlockOptionalMeta bom : lookup.blocks()) { for (BlockState state : bom.getAllBlockStates()) { isInFilter[Block.BLOCK_STATE_REGISTRY.getId(state)] = true; } } return isInFilter; } /** * cheats to get the actual map of id -> blockstate from the various palette implementations */ private static BlockState[] getPalette(Palette<BlockState> palette) { if (palette instanceof GlobalPalette) { // copying the entire registry is not nice so we treat it as a special case return PALETTE_REGISTRY_SENTINEL; } else { FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); palette.write(buf); int size = buf.readVarInt(); BlockState[] states = new BlockState[size]; for (int i = 0; i < size; i++) { BlockState state = Block.BLOCK_STATE_REGISTRY.byId(buf.readVarInt()); assert state != null; states[i] = state; } return states; } } } ================================================ FILE: src/main/java/baritone/cache/WaypointCollection.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.api.cache.IWaypoint; import baritone.api.cache.IWaypointCollection; import baritone.api.cache.Waypoint; import baritone.api.utils.BetterBlockPos; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; /** * Waypoints for a world * * @author leijurv */ public class WaypointCollection implements IWaypointCollection { /** * Magic value to detect invalid waypoint files */ private static final long WAYPOINT_MAGIC_VALUE = 121977993584L; // good value private final Path directory; private final Map<IWaypoint.Tag, Set<IWaypoint>> waypoints; WaypointCollection(Path directory) { this.directory = directory; if (!Files.exists(directory)) { try { Files.createDirectories(directory); } catch (IOException ignored) {} } System.out.println("Would save waypoints to " + directory); this.waypoints = new HashMap<>(); load(); } private void load() { for (Waypoint.Tag tag : Waypoint.Tag.values()) { load(tag); } } private synchronized void load(Waypoint.Tag tag) { this.waypoints.put(tag, new HashSet<>()); Path fileName = this.directory.resolve(tag.name().toLowerCase() + ".mp4"); if (!Files.exists(fileName)) { return; } try ( FileInputStream fileIn = new FileInputStream(fileName.toFile()); BufferedInputStream bufIn = new BufferedInputStream(fileIn); DataInputStream in = new DataInputStream(bufIn) ) { long magic = in.readLong(); if (magic != WAYPOINT_MAGIC_VALUE) { throw new IOException("Bad magic value " + magic); } long length = in.readLong(); // Yes I want 9,223,372,036,854,775,807 waypoints, do you not? while (length-- > 0) { String name = in.readUTF(); long creationTimestamp = in.readLong(); int x = in.readInt(); int y = in.readInt(); int z = in.readInt(); this.waypoints.get(tag).add(new Waypoint(name, tag, new BetterBlockPos(x, y, z), creationTimestamp)); } } catch (IOException ignored) {} } private synchronized void save(Waypoint.Tag tag) { Path fileName = this.directory.resolve(tag.name().toLowerCase() + ".mp4"); try ( FileOutputStream fileOut = new FileOutputStream(fileName.toFile()); BufferedOutputStream bufOut = new BufferedOutputStream(fileOut); DataOutputStream out = new DataOutputStream(bufOut) ) { out.writeLong(WAYPOINT_MAGIC_VALUE); out.writeLong(this.waypoints.get(tag).size()); for (IWaypoint waypoint : this.waypoints.get(tag)) { out.writeUTF(waypoint.getName()); out.writeLong(waypoint.getCreationTimestamp()); out.writeInt(waypoint.getLocation().getX()); out.writeInt(waypoint.getLocation().getY()); out.writeInt(waypoint.getLocation().getZ()); } } catch (IOException ex) { ex.printStackTrace(); } } @Override public void addWaypoint(IWaypoint waypoint) { // no need to check for duplicate, because it's a Set not a List if (waypoints.get(waypoint.getTag()).add(waypoint)) { save(waypoint.getTag()); } } @Override public void removeWaypoint(IWaypoint waypoint) { if (waypoints.get(waypoint.getTag()).remove(waypoint)) { save(waypoint.getTag()); } } @Override public IWaypoint getMostRecentByTag(IWaypoint.Tag tag) { // Find a waypoint of the given tag which has the greatest timestamp value, indicating the most recent return this.waypoints.get(tag).stream().min(Comparator.comparingLong(w -> -w.getCreationTimestamp())).orElse(null); } @Override public Set<IWaypoint> getByTag(IWaypoint.Tag tag) { return Collections.unmodifiableSet(this.waypoints.get(tag)); } @Override public Set<IWaypoint> getAllWaypoints() { return this.waypoints.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); } } ================================================ FILE: src/main/java/baritone/cache/WorldData.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.Baritone; import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWaypointCollection; import baritone.api.cache.IWorldData; import net.minecraft.world.level.dimension.DimensionType; import java.nio.file.Path; /** * Data about a world, from baritone's point of view. Includes cached chunks, waypoints, and map data. * * @author leijurv */ public class WorldData implements IWorldData { public final CachedWorld cache; private final WaypointCollection waypoints; //public final MapData map; public final Path directory; public final DimensionType dimension; WorldData(Path directory, DimensionType dimension) { this.directory = directory; this.cache = new CachedWorld(directory.resolve("cache"), dimension); this.waypoints = new WaypointCollection(directory.resolve("waypoints")); this.dimension = dimension; } public void onClose() { Baritone.getExecutor().execute(() -> { System.out.println("Started saving the world in a new thread"); cache.save(); }); } @Override public ICachedWorld getCachedWorld() { return this.cache; } @Override public IWaypointCollection getWaypoints() { return this.waypoints; } } ================================================ FILE: src/main/java/baritone/cache/WorldProvider.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.Baritone; import baritone.api.cache.IWorldProvider; import baritone.api.utils.IPlayerContext; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Tuple; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelResource; import org.apache.commons.lang3.SystemUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Optional; /** * @author Brady * @since 8/4/2018 */ public class WorldProvider implements IWorldProvider { private static final Map<Path, WorldData> worldCache = new HashMap<>(); private final Baritone baritone; private final IPlayerContext ctx; private WorldData currentWorld; /** * This lets us detect a broken load/unload hook. * @see #detectAndHandleBrokenLoading() */ private Level mcWorld; public WorldProvider(Baritone baritone) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); } @Override public final WorldData getCurrentWorld() { this.detectAndHandleBrokenLoading(); return this.currentWorld; } /** * Called when a new world is initialized to discover the * * @param world The new world */ public final void initWorld(Level world) { this.getSaveDirectories(world).ifPresent(dirs -> { final Path worldDir = dirs.getA(); final Path readmeDir = dirs.getB(); try { // lol wtf is this baritone folder in my minecraft save? // good thing we have a readme Files.createDirectories(readmeDir); Files.write( readmeDir.resolve("readme.txt"), "https://github.com/cabaletta/baritone\n".getBytes(StandardCharsets.US_ASCII) ); } catch (IOException ignored) {} // We will actually store the world data in a subfolder: "DIM<id>" final Path worldDataDir = this.getWorldDataDirectory(worldDir, world); try { Files.createDirectories(worldDataDir); } catch (IOException ignored) {} System.out.println("Baritone world data dir: " + worldDataDir); synchronized (worldCache) { this.currentWorld = worldCache.computeIfAbsent(worldDataDir, d -> new WorldData(d, world.dimensionType())); } this.mcWorld = ctx.world(); }); } public final void closeWorld() { WorldData world = this.currentWorld; this.currentWorld = null; this.mcWorld = null; if (world == null) { return; } world.onClose(); } private Path getWorldDataDirectory(Path parent, Level world) { ResourceLocation dimId = world.dimension().location(); int height = world.dimensionType().logicalHeight(); return parent.resolve(dimId.getNamespace()).resolve(dimId.getPath() + "_" + height); } /** * @param world The world * @return An {@link Optional} containing the world's baritone dir and readme dir, or {@link Optional#empty()} if * the world isn't valid for caching. */ private Optional<Tuple<Path, Path>> getSaveDirectories(Level world) { Path worldDir; Path readmeDir; // If there is an integrated server running (Aka Singleplayer) then do magic to find the world save file if (ctx.minecraft().hasSingleplayerServer()) { worldDir = ctx.minecraft().getSingleplayerServer().getWorldPath(LevelResource.ROOT); // Gets the "depth" of this directory relative to the game's run directory, 2 is the location of the world if (worldDir.relativize(ctx.minecraft().gameDirectory.toPath()).getNameCount() != 2) { // subdirectory of the main save directory for this world worldDir = worldDir.getParent(); } worldDir = worldDir.resolve("baritone"); readmeDir = worldDir; } else { // Otherwise, the server must be remote... String folderName; final ServerData serverData = ctx.minecraft().getCurrentServer(); if (serverData != null) { folderName = ctx.minecraft().isConnectedToRealms() ? "realms" : serverData.ip; } else { //replaymod causes null currentServer and false singleplayer. System.out.println("World seems to be a replay. Not loading Baritone cache."); currentWorld = null; mcWorld = ctx.world(); return Optional.empty(); } if (SystemUtils.IS_OS_WINDOWS) { folderName = folderName.replace(":", "_"); } // TODO: This should probably be in "baritone/servers" worldDir = baritone.getDirectory().resolve(folderName); // Just write the readme to the baritone directory instead of each server save in it readmeDir = baritone.getDirectory(); } return Optional.of(new Tuple<>(worldDir, readmeDir)); } /** * Why does this exist instead of fixing the event? Some mods break the event. Lol. */ private void detectAndHandleBrokenLoading() { if (this.mcWorld != ctx.world()) { if (this.currentWorld != null) { System.out.println("mc.world unloaded unnoticed! Unloading Baritone cache now."); closeWorld(); } if (ctx.world() != null) { System.out.println("mc.world loaded unnoticed! Loading Baritone cache now."); initWorld(ctx.world()); } } else if (this.currentWorld == null && ctx.world() != null && (ctx.minecraft().hasSingleplayerServer() || ctx.minecraft().getCurrentServer() != null)) { System.out.println("Retrying to load Baritone cache"); initWorld(ctx.world()); } } } ================================================ FILE: src/main/java/baritone/cache/WorldScanner.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWorldScanner; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; import java.util.*; import java.util.stream.IntStream; public enum WorldScanner implements IWorldScanner { INSTANCE; @Override public List<BlockPos> scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius) { ArrayList<BlockPos> res = new ArrayList<>(); if (filter.blocks().isEmpty()) { return res; } ClientChunkCache chunkProvider = (ClientChunkCache) ctx.world().getChunkSource(); int maxSearchRadiusSq = maxSearchRadius * maxSearchRadius; int playerChunkX = ctx.playerFeet().getX() >> 4; int playerChunkZ = ctx.playerFeet().getZ() >> 4; int playerY = ctx.playerFeet().getY() - ctx.world().dimensionType().minY(); int playerYBlockStateContainerIndex = playerY >> 4; int[] coordinateIterationOrder = IntStream.range(0, ctx.world().dimensionType().height() / 16).boxed().sorted(Comparator.comparingInt(y -> Math.abs(y - playerYBlockStateContainerIndex))).mapToInt(x -> x).toArray(); int searchRadiusSq = 0; boolean foundWithinY = false; while (true) { boolean allUnloaded = true; boolean foundChunks = false; for (int xoff = -searchRadiusSq; xoff <= searchRadiusSq; xoff++) { for (int zoff = -searchRadiusSq; zoff <= searchRadiusSq; zoff++) { int distance = xoff * xoff + zoff * zoff; if (distance != searchRadiusSq) { continue; } foundChunks = true; int chunkX = xoff + playerChunkX; int chunkZ = zoff + playerChunkZ; LevelChunk chunk = chunkProvider.getChunk(chunkX, chunkZ, null, false); if (chunk == null) { continue; } allUnloaded = false; if (scanChunkInto(chunkX << 4, chunkZ << 4, ctx.world().dimensionType().minY(), chunk, filter, res, max, yLevelThreshold, playerY, coordinateIterationOrder)) { foundWithinY = true; } } } if ((allUnloaded && foundChunks) || (res.size() >= max && (searchRadiusSq > maxSearchRadiusSq || (searchRadiusSq > 1 && foundWithinY))) ) { return res; } searchRadiusSq++; } } @Override public List<BlockPos> scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold) { if (filter.blocks().isEmpty()) { return Collections.emptyList(); } ClientChunkCache chunkProvider = (ClientChunkCache) ctx.world().getChunkSource(); LevelChunk chunk = chunkProvider.getChunk(pos.x, pos.z, null, false); int playerY = ctx.playerFeet().getY(); if (chunk == null || chunk.isEmpty()) { return Collections.emptyList(); } ArrayList<BlockPos> res = new ArrayList<>(); scanChunkInto(pos.x << 4, pos.z << 4, ctx.world().dimensionType().minY(), chunk, filter, res, max, yLevelThreshold, playerY, IntStream.range(0, ctx.world().dimensionType().height() / 16).toArray()); return res; } @Override public int repack(IPlayerContext ctx) { return this.repack(ctx, 40); } @Override public int repack(IPlayerContext ctx, int range) { ChunkSource chunkProvider = ctx.world().getChunkSource(); ICachedWorld cachedWorld = ctx.worldData().getCachedWorld(); BetterBlockPos playerPos = ctx.playerFeet(); int playerChunkX = playerPos.getX() >> 4; int playerChunkZ = playerPos.getZ() >> 4; int minX = playerChunkX - range; int minZ = playerChunkZ - range; int maxX = playerChunkX + range; int maxZ = playerChunkZ + range; int queued = 0; for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { LevelChunk chunk = chunkProvider.getChunk(x, z, false); if (chunk != null && !chunk.isEmpty()) { queued++; cachedWorld.queueForPacking(chunk); } } } return queued; } private boolean scanChunkInto(int chunkX, int chunkZ, int minY, LevelChunk chunk, BlockOptionalMetaLookup filter, Collection<BlockPos> result, int max, int yLevelThreshold, int playerY, int[] coordinateIterationOrder) { LevelChunkSection[] chunkInternalStorageArray = chunk.getSections(); boolean foundWithinY = false; for (int y0 : coordinateIterationOrder) { LevelChunkSection section = chunkInternalStorageArray[y0]; if (section == null || section.hasOnlyAir()) { continue; } int yReal = y0 << 4; PalettedContainer<BlockState> bsc = section.getStates(); for (int yy = 0; yy < 16; yy++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { BlockState state = bsc.get(x, yy, z); if (filter.has(state)) { int y = yReal | yy; if (result.size() >= max) { if (Math.abs(y - playerY) < yLevelThreshold) { foundWithinY = true; } else { if (foundWithinY) { // have found within Y in this chunk, so don't need to consider outside Y // TODO continue iteration to one more sorted Y coordinate block return true; } } } result.add(new BlockPos(chunkX | x, y + minY, chunkZ | z)); } } } } } return foundWithinY; } } ================================================ FILE: src/main/java/baritone/command/CommandSystem.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command; import baritone.api.command.ICommandSystem; import baritone.api.command.argparser.IArgParserManager; import baritone.command.argparser.ArgParserManager; /** * @author Brady * @since 10/4/2019 */ public enum CommandSystem implements ICommandSystem { INSTANCE; @Override public IArgParserManager getParserManager() { return ArgParserManager.INSTANCE; } } ================================================ FILE: src/main/java/baritone/command/ExampleBaritoneControl.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.Settings; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.exception.CommandNotEnoughArgumentsException; import baritone.api.command.exception.CommandNotFoundException; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.command.manager.ICommandManager; import baritone.api.event.events.ChatEvent; import baritone.api.event.events.TabCompleteEvent; import baritone.api.utils.Helper; import baritone.api.utils.SettingsUtil; import baritone.behavior.Behavior; import baritone.command.argument.ArgConsumer; import baritone.command.argument.CommandArguments; import baritone.command.manager.CommandManager; import baritone.utils.accessor.IGuiScreen; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.*; import net.minecraft.network.chat.MutableComponent; import net.minecraft.util.Tuple; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Locale; import java.util.stream.Stream; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class ExampleBaritoneControl extends Behavior implements Helper { private static final Settings settings = BaritoneAPI.getSettings(); private final ICommandManager manager; public ExampleBaritoneControl(Baritone baritone) { super(baritone); this.manager = baritone.getCommandManager(); } @Override public void onSendChatMessage(ChatEvent event) { String msg = event.getMessage(); String prefix = settings.prefix.value; boolean forceRun = msg.startsWith(FORCE_COMMAND_PREFIX); if ((settings.prefixControl.value && msg.startsWith(prefix)) || forceRun) { event.cancel(); String commandStr = msg.substring(forceRun ? FORCE_COMMAND_PREFIX.length() : prefix.length()); if (!runCommand(commandStr) && !commandStr.trim().isEmpty()) { new CommandNotFoundException(CommandManager.expand(commandStr).getA()).handle(null, null); } } else if ((settings.chatControl.value || settings.chatControlAnyway.value) && runCommand(msg)) { event.cancel(); } } private void logRanCommand(String command, String rest) { if (settings.echoCommands.value) { String msg = command + rest; String toDisplay = settings.censorRanCommands.value ? command + " ..." : msg; MutableComponent component = Component.literal(String.format("> %s", toDisplay)); component.setStyle(component.getStyle() .withColor(ChatFormatting.WHITE) .withHoverEvent(new HoverEvent( HoverEvent.Action.SHOW_TEXT, Component.literal("Click to rerun command") )) .withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + msg ))); logDirect(component); } } public boolean runCommand(String msg) { if (msg.trim().equalsIgnoreCase("damn")) { logDirect("daniel"); return false; } else if (msg.trim().equalsIgnoreCase("orderpizza")) { try { ((IGuiScreen) ctx.minecraft().screen).openLinkInvoker(new URI("https://www.dominos.com/en/pages/order/")); } catch (NullPointerException | URISyntaxException ignored) {} return false; } if (msg.isEmpty()) { return this.runCommand("help"); } Tuple<String, List<ICommandArgument>> pair = CommandManager.expand(msg); String command = pair.getA(); String rest = msg.substring(pair.getA().length()); ArgConsumer argc = new ArgConsumer(this.manager, pair.getB()); if (!argc.hasAny()) { Settings.Setting setting = settings.byLowerName.get(command.toLowerCase(Locale.US)); if (setting != null) { logRanCommand(command, rest); if (setting.getValueClass() == Boolean.class) { this.manager.execute(String.format("set toggle %s", setting.getName())); } else { this.manager.execute(String.format("set %s", setting.getName())); } return true; } } else if (argc.hasExactlyOne()) { for (Settings.Setting setting : settings.allSettings) { if (setting.isJavaOnly()) { continue; } if (setting.getName().equalsIgnoreCase(pair.getA())) { logRanCommand(command, rest); try { this.manager.execute(String.format("set %s %s", setting.getName(), argc.getString())); } catch (CommandNotEnoughArgumentsException ignored) {} // The operation is safe return true; } } } // If the command exists, then handle echoing the input if (this.manager.getCommand(pair.getA()) != null) { logRanCommand(command, rest); } return this.manager.execute(pair); } @Override public void onPreTabComplete(TabCompleteEvent event) { if (!settings.prefixControl.value) { return; } String prefix = event.prefix; String commandPrefix = settings.prefix.value; if (!prefix.startsWith(commandPrefix)) { return; } String msg = prefix.substring(commandPrefix.length()); List<ICommandArgument> args = CommandArguments.from(msg, true); Stream<String> stream = tabComplete(msg); if (args.size() == 1) { stream = stream.map(x -> commandPrefix + x); } event.completions = stream.toArray(String[]::new); } public Stream<String> tabComplete(String msg) { try { List<ICommandArgument> args = CommandArguments.from(msg, true); ArgConsumer argc = new ArgConsumer(this.manager, args); if (argc.hasAtMost(2)) { if (argc.hasExactly(1)) { return new TabCompleteHelper() .addCommands(this.manager) .addSettings() .filterPrefix(argc.getString()) .stream(); } Settings.Setting setting = settings.byLowerName.get(argc.getString().toLowerCase(Locale.US)); if (setting != null && !setting.isJavaOnly()) { if (setting.getValueClass() == Boolean.class) { TabCompleteHelper helper = new TabCompleteHelper(); if ((Boolean) setting.value) { helper.append("true", "false"); } else { helper.append("false", "true"); } return helper.filterPrefix(argc.getString()).stream(); } else { return Stream.of(SettingsUtil.settingValueToString(setting)); } } } return this.manager.tabComplete(msg); } catch (CommandNotEnoughArgumentsException ignored) { // Shouldn't happen, the operation is safe return Stream.empty(); } } } ================================================ FILE: src/main/java/baritone/command/argparser/ArgParserManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.argparser; import baritone.api.command.argparser.IArgParser; import baritone.api.command.argparser.IArgParserManager; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.exception.CommandNoParserForTypeException; import baritone.api.command.registry.Registry; public enum ArgParserManager implements IArgParserManager { INSTANCE; public final Registry<IArgParser> registry = new Registry<>(); ArgParserManager() { DefaultArgParsers.ALL.forEach(this.registry::register); } @Override public <T> IArgParser.Stateless<T> getParserStateless(Class<T> type) { //noinspection unchecked return this.registry.descendingStream() .filter(IArgParser.Stateless.class::isInstance) .map(IArgParser.Stateless.class::cast) .filter(parser -> parser.getTarget().isAssignableFrom(type)) .findFirst() .orElse(null); } @Override public <T, S> IArgParser.Stated<T, S> getParserStated(Class<T> type, Class<S> stateKlass) { //noinspection unchecked return this.registry.descendingStream() .filter(IArgParser.Stated.class::isInstance) .map(IArgParser.Stated.class::cast) .filter(parser -> parser.getTarget().isAssignableFrom(type)) .filter(parser -> parser.getStateType().isAssignableFrom(stateKlass)) .map(IArgParser.Stated.class::cast) .findFirst() .orElse(null); } @Override public <T> T parseStateless(Class<T> type, ICommandArgument arg) throws CommandInvalidTypeException { IArgParser.Stateless<T> parser = this.getParserStateless(type); if (parser == null) { throw new CommandNoParserForTypeException(type); } try { return parser.parseArg(arg); } catch (Exception exc) { throw new CommandInvalidTypeException(arg, type.getSimpleName()); } } @Override public <T, S> T parseStated(Class<T> type, Class<S> stateKlass, ICommandArgument arg, S state) throws CommandInvalidTypeException { IArgParser.Stated<T, S> parser = this.getParserStated(type, stateKlass); if (parser == null) { throw new CommandNoParserForTypeException(type); } try { return parser.parseArg(arg, state); } catch (Exception exc) { throw new CommandInvalidTypeException(arg, type.getSimpleName()); } } @Override public Registry<IArgParser> getRegistry() { return this.registry; } } ================================================ FILE: src/main/java/baritone/command/argparser/DefaultArgParsers.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.argparser; import baritone.api.command.argparser.IArgParser; import baritone.api.command.argument.ICommandArgument; import java.util.Arrays; import java.util.List; import java.util.Locale; public class DefaultArgParsers { public enum IntArgumentParser implements IArgParser.Stateless<Integer> { INSTANCE; @Override public Class<Integer> getTarget() { return Integer.class; } @Override public Integer parseArg(ICommandArgument arg) throws RuntimeException { return Integer.parseInt(arg.getValue()); } } public enum LongArgumentParser implements IArgParser.Stateless<Long> { INSTANCE; @Override public Class<Long> getTarget() { return Long.class; } @Override public Long parseArg(ICommandArgument arg) throws RuntimeException { return Long.parseLong(arg.getValue()); } } public enum FloatArgumentParser implements IArgParser.Stateless<Float> { INSTANCE; @Override public Class<Float> getTarget() { return Float.class; } @Override public Float parseArg(ICommandArgument arg) throws RuntimeException { String value = arg.getValue(); if (!value.matches("^([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)|)$")) { throw new IllegalArgumentException("failed float format check"); } return Float.parseFloat(value); } } public enum DoubleArgumentParser implements IArgParser.Stateless<Double> { INSTANCE; @Override public Class<Double> getTarget() { return Double.class; } @Override public Double parseArg(ICommandArgument arg) throws RuntimeException { String value = arg.getValue(); if (!value.matches("^([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)|)$")) { throw new IllegalArgumentException("failed double format check"); } return Double.parseDouble(value); } } public static class BooleanArgumentParser implements IArgParser.Stateless<Boolean> { public static final BooleanArgumentParser INSTANCE = new BooleanArgumentParser(); public static final List<String> TRUTHY_VALUES = Arrays.asList("1", "true", "yes", "t", "y", "on", "enable"); public static final List<String> FALSY_VALUES = Arrays.asList("0", "false", "no", "f", "n", "off", "disable"); @Override public Class<Boolean> getTarget() { return Boolean.class; } @Override public Boolean parseArg(ICommandArgument arg) throws RuntimeException { String value = arg.getValue(); if (TRUTHY_VALUES.contains(value.toLowerCase(Locale.US))) { return true; } else if (FALSY_VALUES.contains(value.toLowerCase(Locale.US))) { return false; } else { throw new IllegalArgumentException("invalid boolean"); } } } public static final List<IArgParser<?>> ALL = Arrays.asList( IntArgumentParser.INSTANCE, LongArgumentParser.INSTANCE, FloatArgumentParser.INSTANCE, DoubleArgumentParser.INSTANCE, BooleanArgumentParser.INSTANCE ); } ================================================ FILE: src/main/java/baritone/command/argument/ArgConsumer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.argument; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.datatypes.IDatatype; import baritone.api.command.datatypes.IDatatypeContext; import baritone.api.command.datatypes.IDatatypeFor; import baritone.api.command.datatypes.IDatatypePost; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.exception.CommandNotEnoughArgumentsException; import baritone.api.command.exception.CommandTooManyArgumentsException; import baritone.api.command.manager.ICommandManager; import java.util.ArrayList; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.stream.Stream; public class ArgConsumer implements IArgConsumer { /** * The parent {@link ICommandManager} for this {@link IArgConsumer}}. Used by {@link #context}. */ private final ICommandManager manager; /** * The {@link IDatatypeContext} instance for this {@link IArgConsumer}}, passed to * datatypes when an operation is performed upon them. * * @see IDatatype * @see IDatatypeContext */ private final IDatatypeContext context; /** * The list of arguments in this ArgConsumer */ private final LinkedList<ICommandArgument> args; /** * The list of consumed arguments for this ArgConsumer. The most recently consumed argument is the last one */ private final Deque<ICommandArgument> consumed; private ArgConsumer(ICommandManager manager, Deque<ICommandArgument> args, Deque<ICommandArgument> consumed) { this.manager = manager; this.context = this.new Context(); this.args = new LinkedList<>(args); this.consumed = new LinkedList<>(consumed); } public ArgConsumer(ICommandManager manager, List<ICommandArgument> args) { this(manager, new LinkedList<>(args), new LinkedList<>()); } @Override public LinkedList<ICommandArgument> getArgs() { return this.args; } @Override public Deque<ICommandArgument> getConsumed() { return this.consumed; } @Override public boolean has(int num) { return args.size() >= num; } @Override public boolean hasAny() { return has(1); } @Override public boolean hasAtMost(int num) { return args.size() <= num; } @Override public boolean hasAtMostOne() { return hasAtMost(1); } @Override public boolean hasExactly(int num) { return args.size() == num; } @Override public boolean hasExactlyOne() { return hasExactly(1); } @Override public ICommandArgument peek(int index) throws CommandNotEnoughArgumentsException { requireMin(index + 1); return args.get(index); } @Override public ICommandArgument peek() throws CommandNotEnoughArgumentsException { return peek(0); } @Override public boolean is(Class<?> type, int index) throws CommandNotEnoughArgumentsException { return peek(index).is(type); } @Override public boolean is(Class<?> type) throws CommandNotEnoughArgumentsException { return is(type, 0); } @Override public String peekString(int index) throws CommandNotEnoughArgumentsException { return peek(index).getValue(); } @Override public String peekString() throws CommandNotEnoughArgumentsException { return peekString(0); } @Override public <E extends Enum<?>> E peekEnum(Class<E> enumClass, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return peek(index).getEnum(enumClass); } @Override public <E extends Enum<?>> E peekEnum(Class<E> enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return peekEnum(enumClass, 0); } @Override public <E extends Enum<?>> E peekEnumOrNull(Class<E> enumClass, int index) throws CommandNotEnoughArgumentsException { try { return peekEnum(enumClass, index); } catch (CommandInvalidTypeException e) { return null; } } @Override public <E extends Enum<?>> E peekEnumOrNull(Class<E> enumClass) throws CommandNotEnoughArgumentsException { return peekEnumOrNull(enumClass, 0); } @Override public <T> T peekAs(Class<T> type, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return peek(index).getAs(type); } @Override public <T> T peekAs(Class<T> type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return peekAs(type, 0); } @Override public <T> T peekAsOrDefault(Class<T> type, T def, int index) throws CommandNotEnoughArgumentsException { try { return peekAs(type, index); } catch (CommandInvalidTypeException e) { return def; } } @Override public <T> T peekAsOrDefault(Class<T> type, T def) throws CommandNotEnoughArgumentsException { return peekAsOrDefault(type, def, 0); } @Override public <T> T peekAsOrNull(Class<T> type, int index) throws CommandNotEnoughArgumentsException { return peekAsOrDefault(type, null, index); } @Override public <T> T peekAsOrNull(Class<T> type) throws CommandNotEnoughArgumentsException { return peekAsOrNull(type, 0); } @Override public <T> T peekDatatype(IDatatypeFor<T> datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return copy().getDatatypeFor(datatype); } @Override public <T, O> T peekDatatype(IDatatypePost<T, O> datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return this.peekDatatype(datatype, null); } @Override public <T, O> T peekDatatype(IDatatypePost<T, O> datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return copy().getDatatypePost(datatype, original); } @Override public <T> T peekDatatypeOrNull(IDatatypeFor<T> datatype) { return copy().getDatatypeForOrNull(datatype); } @Override public <T, O> T peekDatatypeOrNull(IDatatypePost<T, O> datatype) { return copy().getDatatypePostOrNull(datatype, null); } @Override public <T, O, D extends IDatatypePost<T, O>> T peekDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return copy().getDatatypePost(datatype, original); } @Override public <T, O, D extends IDatatypePost<T, O>> T peekDatatypePostOrDefault(D datatype, O original, T def) { return copy().getDatatypePostOrDefault(datatype, original, def); } @Override public <T, O, D extends IDatatypePost<T, O>> T peekDatatypePostOrNull(D datatype, O original) { return peekDatatypePostOrDefault(datatype, original, null); } @Override public <T, D extends IDatatypeFor<T>> T peekDatatypeFor(Class<D> datatype) { return copy().peekDatatypeFor(datatype); } @Override public <T, D extends IDatatypeFor<T>> T peekDatatypeForOrDefault(Class<D> datatype, T def) { return copy().peekDatatypeForOrDefault(datatype, def); } @Override public <T, D extends IDatatypeFor<T>> T peekDatatypeForOrNull(Class<D> datatype) { return peekDatatypeForOrDefault(datatype, null); } @Override public ICommandArgument get() throws CommandNotEnoughArgumentsException { requireMin(1); ICommandArgument arg = args.removeFirst(); consumed.add(arg); return arg; } @Override public String getString() throws CommandNotEnoughArgumentsException { return get().getValue(); } @Override public <E extends Enum<?>> E getEnum(Class<E> enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return get().getEnum(enumClass); } @Override public <E extends Enum<?>> E getEnumOrDefault(Class<E> enumClass, E def) throws CommandNotEnoughArgumentsException { try { peekEnum(enumClass); return getEnum(enumClass); } catch (CommandInvalidTypeException e) { return def; } } @Override public <E extends Enum<?>> E getEnumOrNull(Class<E> enumClass) throws CommandNotEnoughArgumentsException { return getEnumOrDefault(enumClass, null); } @Override public <T> T getAs(Class<T> type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { return get().getAs(type); } @Override public <T> T getAsOrDefault(Class<T> type, T def) throws CommandNotEnoughArgumentsException { try { T val = peek().getAs(type); get(); return val; } catch (CommandInvalidTypeException e) { return def; } } @Override public <T> T getAsOrNull(Class<T> type) throws CommandNotEnoughArgumentsException { return getAsOrDefault(type, null); } @Override public <T, O, D extends IDatatypePost<T, O>> T getDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { try { return datatype.apply(this.context, original); } catch (Exception e) { if (Baritone.settings().verboseCommandExceptions.value) { e.printStackTrace(); } throw new CommandInvalidTypeException(hasAny() ? peek() : consumed(), datatype.getClass().getSimpleName(), e); } } @Override public <T, O, D extends IDatatypePost<T, O>> T getDatatypePostOrDefault(D datatype, O original, T _default) { final List<ICommandArgument> argsSnapshot = new ArrayList<>(this.args); final List<ICommandArgument> consumedSnapshot = new ArrayList<>(this.consumed); try { return this.getDatatypePost(datatype, original); } catch (Exception e) { this.args.clear(); this.args.addAll(argsSnapshot); this.consumed.clear(); this.consumed.addAll(consumedSnapshot); return _default; } } @Override public <T, O, D extends IDatatypePost<T, O>> T getDatatypePostOrNull(D datatype, O original) { return this.getDatatypePostOrDefault(datatype, original, null); } @Override public <T, D extends IDatatypeFor<T>> T getDatatypeFor(D datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { try { return datatype.get(this.context); } catch (Exception e) { if (Baritone.settings().verboseCommandExceptions.value) { e.printStackTrace(); } throw new CommandInvalidTypeException(hasAny() ? peek() : consumed(), datatype.getClass().getSimpleName(), e); } } @Override public <T, D extends IDatatypeFor<T>> T getDatatypeForOrDefault(D datatype, T def) { final List<ICommandArgument> argsSnapshot = new ArrayList<>(this.args); final List<ICommandArgument> consumedSnapshot = new ArrayList<>(this.consumed); try { return this.getDatatypeFor(datatype); } catch (Exception e) { this.args.clear(); this.args.addAll(argsSnapshot); this.consumed.clear(); this.consumed.addAll(consumedSnapshot); return def; } } @Override public <T, D extends IDatatypeFor<T>> T getDatatypeForOrNull(D datatype) { return this.getDatatypeForOrDefault(datatype, null); } @Override public <T extends IDatatype> Stream<String> tabCompleteDatatype(T datatype) { try { return datatype.tabComplete(this.context); } catch (CommandException ignored) { // NOP } catch (Exception e) { e.printStackTrace(); } return Stream.empty(); } @Override public String rawRest() { return args.size() > 0 ? args.getFirst().getRawRest() : ""; } @Override public void requireMin(int min) throws CommandNotEnoughArgumentsException { if (args.size() < min) { throw new CommandNotEnoughArgumentsException(min + consumed.size()); } } @Override public void requireMax(int max) throws CommandTooManyArgumentsException { if (args.size() > max) { throw new CommandTooManyArgumentsException(max + consumed.size()); } } @Override public void requireExactly(int args) throws CommandException { requireMin(args); requireMax(args); } @Override public boolean hasConsumed() { return !consumed.isEmpty(); } @Override public ICommandArgument consumed() { return consumed.size() > 0 ? consumed.getLast() : CommandArguments.unknown(); } @Override public String consumedString() { return consumed().getValue(); } @Override public ArgConsumer copy() { return new ArgConsumer(manager, args, consumed); } /** * Implementation of {@link IDatatypeContext} which adapts to the parent {@link IArgConsumer}} */ private final class Context implements IDatatypeContext { @Override public final IBaritone getBaritone() { return ArgConsumer.this.manager.getBaritone(); } @Override public final ArgConsumer getConsumer() { return ArgConsumer.this; } } } ================================================ FILE: src/main/java/baritone/command/argument/CommandArgument.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.argument; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.command.argparser.ArgParserManager; import java.util.stream.Stream; /** * The default implementation of {@link ICommandArgument} * * @author LoganDark */ class CommandArgument implements ICommandArgument { private final int index; private final String value; private final String rawRest; CommandArgument(int index, String value, String rawRest) { this.index = index; this.value = value; this.rawRest = rawRest; } @Override public int getIndex() { return this.index; } @Override public String getValue() { return this.value; } @Override public String getRawRest() { return this.rawRest; } @Override public <E extends Enum<?>> E getEnum(Class<E> enumClass) throws CommandInvalidTypeException { return Stream.of(enumClass.getEnumConstants()) .filter(e -> e.name().equalsIgnoreCase(value)) .findFirst() .orElseThrow(() -> new CommandInvalidTypeException(this, enumClass.getSimpleName())); } @Override public <T> T getAs(Class<T> type) throws CommandInvalidTypeException { return ArgParserManager.INSTANCE.parseStateless(type, this); } @Override public <T> boolean is(Class<T> type) { try { getAs(type); return true; } catch (Throwable t) { return false; } } @SuppressWarnings("UnusedReturnValue") @Override public <T, S> T getAs(Class<T> type, Class<S> stateType, S state) throws CommandInvalidTypeException { return ArgParserManager.INSTANCE.parseStated(type, stateType, this, state); } @Override public <T, S> boolean is(Class<T> type, Class<S> stateType, S state) { try { getAs(type, stateType, state); return true; } catch (Throwable t) { return false; } } } ================================================ FILE: src/main/java/baritone/command/argument/CommandArguments.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.argument; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.exception.CommandInvalidArgumentException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author LoganDark */ public final class CommandArguments { private CommandArguments() {} private static final Pattern ARG_PATTERN = Pattern.compile("\\S+"); /** * Turn a string into a list of {@link ICommandArgument}s. This is needed because of {@link ICommandArgument#getRawRest()} * * @param string The string to convert * @param preserveEmptyLast If the string ends with whitespace, add an empty {@link ICommandArgument} to the end This * is useful for tab completion * @return A list of {@link ICommandArgument}s */ public static List<ICommandArgument> from(String string, boolean preserveEmptyLast) { List<ICommandArgument> args = new ArrayList<>(); Matcher argMatcher = ARG_PATTERN.matcher(string); int lastEnd = -1; while (argMatcher.find()) { args.add(new CommandArgument( args.size(), argMatcher.group(), string.substring(argMatcher.start()) )); lastEnd = argMatcher.end(); } if (preserveEmptyLast && lastEnd < string.length()) { args.add(new CommandArgument(args.size(), "", "")); } return args; } /** * @see #from(String, boolean) */ public static List<ICommandArgument> from(String string) { return from(string, false); } /** * Returns an "unknown" {@link CommandArgument}. This shouldn't be used unless you absolutely have no information - * ESPECIALLY not with {@link CommandInvalidArgumentException}s * * @return The unknown {@link CommandArgument} */ public static CommandArgument unknown() { return new CommandArgument(-1, "<unknown>", ""); } } ================================================ FILE: src/main/java/baritone/command/defaults/AxisCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalAxis; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class AxisCommand extends Command { public AxisCommand(IBaritone baritone) { super(baritone, "axis", "highway"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); Goal goal = new GoalAxis(); baritone.getCustomGoalProcess().setGoal(goal); logDirect(String.format("Goal: %s", goal.toString())); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Set a goal to the axes"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The axis command sets a goal that tells Baritone to head towards the nearest axis. That is, X=0 or Z=0.", "", "Usage:", "> axis" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/BlacklistCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.process.IGetToBlockProcess; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class BlacklistCommand extends Command { public BlacklistCommand(IBaritone baritone) { super(baritone, "blacklist"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); IGetToBlockProcess proc = baritone.getGetToBlockProcess(); if (!proc.isActive()) { throw new CommandInvalidStateException("GetToBlockProcess is not currently active"); } if (proc.blacklistClosest()) { logDirect("Blacklisted closest instances"); } else { throw new CommandInvalidStateException("No known locations, unable to blacklist"); } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Blacklist closest block"; } @Override public List<String> getLongDesc() { return Arrays.asList( "While going to a block this command blacklists the closest block so that Baritone won't attempt to get to it.", "", "Usage:", "> blacklist" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/BuildCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.RelativeBlockPos; import baritone.api.command.datatypes.RelativeFile; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.utils.BetterBlockPos; import baritone.utils.schematic.SchematicSystem; import org.apache.commons.io.FilenameUtils; import java.io.File; import java.util.Arrays; import java.util.List; import java.util.StringJoiner; import java.util.stream.Stream; public class BuildCommand extends Command { private final File schematicsDir; public BuildCommand(IBaritone baritone) { super(baritone, "build"); this.schematicsDir = new File(baritone.getPlayerContext().minecraft().gameDirectory, "schematics"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { final File file0 = args.getDatatypePost(RelativeFile.INSTANCE, schematicsDir).getAbsoluteFile(); File file = file0; if (FilenameUtils.getExtension(file.getAbsolutePath()).isEmpty()) { file = new File(file.getAbsolutePath() + "." + Baritone.settings().schematicFallbackExtension.value); } if (!file.exists()) { if (file0.exists()) { throw new CommandInvalidStateException(String.format( "Cannot load %s because I do not know which schematic format" + " that is. Please rename the file to include the correct" + " file extension.", file)); } throw new CommandInvalidStateException("Cannot find " + file); } if (!SchematicSystem.INSTANCE.getByFile(file).isPresent()) { StringJoiner formats = new StringJoiner(", "); SchematicSystem.INSTANCE.getFileExtensions().forEach(formats::add); throw new CommandInvalidStateException(String.format( "Unsupported schematic format. Reckognized file extensions are: %s", formats )); } BetterBlockPos origin = ctx.playerFeet(); BetterBlockPos buildOrigin; if (args.hasAny()) { args.requireMax(3); buildOrigin = args.getDatatypePost(RelativeBlockPos.INSTANCE, origin); } else { args.requireMax(0); buildOrigin = origin; } boolean success = baritone.getBuilderProcess().build(file.getName(), file, buildOrigin); if (!success) { throw new CommandInvalidStateException("Couldn't load the schematic. Either your schematic is corrupt or this is a bug."); } logDirect(String.format("Successfully loaded schematic for building\nOrigin: %s", buildOrigin)); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasExactlyOne()) { return RelativeFile.tabComplete(args, schematicsDir); } else if (args.has(2)) { args.get(); return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); } return Stream.empty(); } @Override public String getShortDesc() { return "Build a schematic"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Build a schematic from a file.", "", "Usage:", "> build <filename> - Loads and builds '<filename>.schematic'", "> build <filename> <x> <y> <z> - Custom position" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ClickCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ClickCommand extends Command { public ClickCommand(IBaritone baritone) { super(baritone, "click"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); baritone.openClick(); logDirect("aight dude"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Open click"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Opens click dude", "", "Usage:", "> click" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ComeCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalBlock; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ComeCommand extends Command { public ComeCommand(IBaritone baritone) { super(baritone, "come"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); baritone.getCustomGoalProcess().setGoalAndPath(new GoalBlock(ctx.viewerPos())); logDirect("Coming"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Start heading towards your camera"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The come command tells Baritone to head towards your camera.", "", "This can be useful in hacked clients where freecam doesn't move your player position.", "", "Usage:", "> come" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/CommandAlias.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import java.util.Collections; import java.util.List; import java.util.stream.Stream; public class CommandAlias extends Command { private final String shortDesc; public final String target; public CommandAlias(IBaritone baritone, List<String> names, String shortDesc, String target) { super(baritone, names.toArray(new String[0])); this.shortDesc = shortDesc; this.target = target; } public CommandAlias(IBaritone baritone, String name, String shortDesc, String target) { super(baritone, name); this.shortDesc = shortDesc; this.target = target; } @Override public void execute(String label, IArgConsumer args) { this.baritone.getCommandManager().execute(String.format("%s %s", target, args.rawRest())); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return this.baritone.getCommandManager().tabComplete(String.format("%s %s", target, args.rawRest())); } @Override public String getShortDesc() { return shortDesc; } @Override public List<String> getLongDesc() { return Collections.singletonList(String.format("This command is an alias, for: %s ...", target)); } } ================================================ FILE: src/main/java/baritone/command/defaults/DefaultCommands.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.ICommand; import java.util.*; public final class DefaultCommands { private DefaultCommands() { } public static List<ICommand> createAll(IBaritone baritone) { Objects.requireNonNull(baritone); List<ICommand> commands = new ArrayList<>(Arrays.asList( new HelpCommand(baritone), new SetCommand(baritone), new CommandAlias(baritone, Arrays.asList("modified", "mod", "baritone", "modifiedsettings"), "List modified settings", "set modified"), new CommandAlias(baritone, "reset", "Reset all settings or just one", "set reset"), new GoalCommand(baritone), new GotoCommand(baritone), new PathCommand(baritone), new ProcCommand(baritone), new ETACommand(baritone), new VersionCommand(baritone), new RepackCommand(baritone), new BuildCommand(baritone), //new SchematicaCommand(baritone), new LitematicaCommand(baritone), new ComeCommand(baritone), new AxisCommand(baritone), new ForceCancelCommand(baritone), new GcCommand(baritone), new InvertCommand(baritone), new TunnelCommand(baritone), new RenderCommand(baritone), new FarmCommand(baritone), new FollowCommand(baritone), new PickupCommand(baritone), new ExploreFilterCommand(baritone), new ReloadAllCommand(baritone), new SaveAllCommand(baritone), new ExploreCommand(baritone), new BlacklistCommand(baritone), new FindCommand(baritone), new MineCommand(baritone), new ClickCommand(baritone), new SurfaceCommand(baritone), new ThisWayCommand(baritone), new WaypointsCommand(baritone), new CommandAlias(baritone, "sethome", "Sets your home waypoint", "waypoints save home"), new CommandAlias(baritone, "home", "Path to your home waypoint", "waypoints goto home"), new SelCommand(baritone), new ElytraCommand(baritone) )); ExecutionControlCommands prc = new ExecutionControlCommands(baritone); commands.add(prc.pauseCommand); commands.add(prc.resumeCommand); commands.add(prc.pausedCommand); commands.add(prc.cancelCommand); return Collections.unmodifiableList(commands); } } ================================================ FILE: src/main/java/baritone/command/defaults/ETACommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.behavior.IPathingBehavior; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.pathing.calc.IPathingControlManager; import baritone.api.process.IBaritoneProcess; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ETACommand extends Command { public ETACommand(IBaritone baritone) { super(baritone, "eta"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); IPathingControlManager pathingControlManager = baritone.getPathingControlManager(); IBaritoneProcess process = pathingControlManager.mostRecentInControl().orElse(null); if (process == null) { throw new CommandInvalidStateException("No process in control"); } IPathingBehavior pathingBehavior = baritone.getPathingBehavior(); double ticksRemainingInSegment = pathingBehavior.ticksRemainingInSegment().orElse(Double.NaN); double ticksRemainingInGoal = pathingBehavior.estimatedTicksToGoal().orElse(Double.NaN); logDirect(String.format( "Next segment: %.1fs (%.0f ticks)\n" + "Goal: %.1fs (%.0f ticks)", ticksRemainingInSegment / 20, // we just assume tps is 20, it isn't worth the effort that is needed to calculate it exactly ticksRemainingInSegment, ticksRemainingInGoal / 20, ticksRemainingInGoal )); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "View the current ETA"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The ETA command provides information about the estimated time until the next segment.", "and the goal", "", "Be aware that the ETA to your goal is really unprecise", "", "Usage:", "> eta - View ETA, if present" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ElytraCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.pathing.goals.Goal; import baritone.api.process.ICustomGoalProcess; import baritone.api.process.IElytraProcess; import net.minecraft.ChatFormatting; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import net.minecraft.world.level.Level; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class ElytraCommand extends Command { public ElytraCommand(IBaritone baritone) { super(baritone, "elytra"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { final ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); final IElytraProcess elytra = baritone.getElytraProcess(); if (args.hasExactlyOne() && args.peekString().equals("supported")) { logDirect(elytra.isLoaded() ? "yes" : unsupportedSystemMessage()); return; } if (!elytra.isLoaded()) { throw new CommandInvalidStateException(unsupportedSystemMessage()); } if (!args.hasAny()) { if (Baritone.settings().elytraTermsAccepted.value) { if (detectOn2b2t()) { warn2b2t(); } } else { gatekeep(); } Goal iGoal = customGoalProcess.mostRecentGoal(); if (iGoal == null) { throw new CommandInvalidStateException("No goal has been set"); } if (ctx.world().dimension() != Level.NETHER) { throw new CommandInvalidStateException("Only works in the nether"); } try { elytra.pathTo(iGoal); } catch (IllegalArgumentException ex) { throw new CommandInvalidStateException(ex.getMessage()); } return; } final String action = args.getString(); switch (action) { case "reset": { elytra.resetState(); logDirect("Reset state but still flying to same goal"); break; } case "repack": { elytra.repackChunks(); logDirect("Queued all loaded chunks for repacking"); break; } default: { throw new CommandInvalidStateException("Invalid action"); } } } private void warn2b2t() { if (Baritone.settings().elytraPredictTerrain.value) { long seed = Baritone.settings().elytraNetherSeed.value; if (seed != NEW_2B2T_SEED && seed != OLD_2B2T_SEED) { logDirect(Component.literal("It looks like you're on 2b2t, but elytraNetherSeed is incorrect.")); // match color logDirect(suggest2b2tSeeds()); } } } private Component suggest2b2tSeeds() { MutableComponent clippy = Component.literal(""); clippy.append("Within a few hundred blocks of spawn/axis/highways/etc, the terrain is too fragmented to be predictable. Baritone Elytra will still work, just with backtracking. "); clippy.append("However, once you get more than a few thousand blocks out, you should try "); MutableComponent olderSeed = Component.literal("the older seed (click here)"); olderSeed.setStyle(olderSeed.getStyle().withUnderlined(true).withBold(true).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraNetherSeed " + OLD_2B2T_SEED))).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + "set elytraNetherSeed " + OLD_2B2T_SEED))); clippy.append(olderSeed); clippy.append(". Once you're further out into newer terrain generation (this includes everything up through 1.12), you should try "); MutableComponent newerSeed = Component.literal("the newer seed (click here)"); newerSeed.setStyle(newerSeed.getStyle().withUnderlined(true).withBold(true).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraNetherSeed " + NEW_2B2T_SEED))).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + "set elytraNetherSeed " + NEW_2B2T_SEED))); clippy.append(newerSeed); clippy.append(". Once you get into 1.19 terrain, the terrain becomes unpredictable again, due to custom non-vanilla generation, and you should set #elytraPredictTerrain to false. "); return clippy; } private void gatekeep() { MutableComponent gatekeep = Component.literal(""); gatekeep.append("To disable this message, enable the setting elytraTermsAccepted\n"); gatekeep.append("Baritone Elytra is an experimental feature. It is only intended for long distance travel in the Nether using fireworks for vanilla boost. It will not work with any other mods (\"hacks\") for non-vanilla boost. "); MutableComponent gatekeep2 = Component.literal("If you want Baritone to attempt to take off from the ground for you, you can enable the elytraAutoJump setting (not advisable on laggy servers!). "); gatekeep2.setStyle(gatekeep2.getStyle().withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraAutoJump true")))); gatekeep.append(gatekeep2); MutableComponent gatekeep3 = Component.literal("If you want Baritone to go slower, enable the elytraConserveFireworks setting and/or decrease the elytraFireworkSpeed setting. "); gatekeep3.setStyle(gatekeep3.getStyle().withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraConserveFireworks true\n" + Baritone.settings().prefix.value + "set elytraFireworkSpeed 0.6\n(the 0.6 number is just an example, tweak to your liking)")))); gatekeep.append(gatekeep3); MutableComponent gatekeep4 = Component.literal("Baritone Elytra "); MutableComponent red = Component.literal("wants to know the seed"); red.setStyle(red.getStyle().withColor(ChatFormatting.RED).withUnderlined(true).withBold(true)); gatekeep4.append(red); gatekeep4.append(" of the world you are in. If it doesn't have the correct seed, it will frequently backtrack. It uses the seed to generate terrain far beyond what you can see, since terrain obstacles in the Nether can be much larger than your render distance. "); gatekeep.append(gatekeep4); gatekeep.append("\n"); if (detectOn2b2t()) { MutableComponent gatekeep5 = Component.literal("It looks like you're on 2b2t. "); gatekeep5.append(suggest2b2tSeeds()); if (!Baritone.settings().elytraPredictTerrain.value) { gatekeep5.append(Baritone.settings().prefix.value + "elytraPredictTerrain is currently disabled. "); } else { if (Baritone.settings().elytraNetherSeed.value == NEW_2B2T_SEED) { gatekeep5.append("You are using the newer seed. "); } else if (Baritone.settings().elytraNetherSeed.value == OLD_2B2T_SEED) { gatekeep5.append("You are using the older seed. "); } else { gatekeep5.append("Defaulting to the newer seed. "); Baritone.settings().elytraNetherSeed.value = NEW_2B2T_SEED; } } gatekeep.append(gatekeep5); } else { if (Baritone.settings().elytraNetherSeed.value == NEW_2B2T_SEED) { MutableComponent gatekeep5 = Component.literal("Baritone doesn't know the seed of your world. Set it with: " + Baritone.settings().prefix.value + "set elytraNetherSeed seedgoeshere\n"); gatekeep5.append("For the time being, elytraPredictTerrain is defaulting to false since the seed is unknown."); gatekeep.append(gatekeep5); Baritone.settings().elytraPredictTerrain.value = false; } else { if (Baritone.settings().elytraPredictTerrain.value) { MutableComponent gatekeep5 = Component.literal("Baritone Elytra is predicting terrain assuming that " + Baritone.settings().elytraNetherSeed.value + " is the correct seed. Change that with " + Baritone.settings().prefix.value + "set elytraNetherSeed seedgoeshere, or disable it with " + Baritone.settings().prefix.value + "set elytraPredictTerrain false"); gatekeep.append(gatekeep5); } else { MutableComponent gatekeep5 = Component.literal("Baritone Elytra is not predicting terrain. If you don't know the seed, this is the correct thing to do. If you do know the seed, input it with " + Baritone.settings().prefix.value + "set elytraNetherSeed seedgoeshere, and then enable it with " + Baritone.settings().prefix.value + "set elytraPredictTerrain true"); gatekeep.append(gatekeep5); } } } logDirect(gatekeep); } private boolean detectOn2b2t() { ServerData data = ctx.minecraft().getCurrentServer(); return data != null && data.ip.toLowerCase().contains("2b2t.org"); } private static final long OLD_2B2T_SEED = -4100785268875389365L; private static final long NEW_2B2T_SEED = 146008555100680L; @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { TabCompleteHelper helper = new TabCompleteHelper(); if (args.hasExactlyOne()) { helper.append("reset", "repack", "supported"); } return helper.filterPrefix(args.getString()).stream(); } @Override public String getShortDesc() { return "elytra time"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The elytra command tells baritone to, in the nether, automatically fly to the current goal.", "", "Usage:", "> elytra - fly to the current goal", "> elytra reset - Resets the state of the process, but will try to keep flying to the same goal.", "> elytra repack - Queues all of the chunks in render distance to be given to the native library.", "> elytra supported - Tells you if baritone ships a native library that is compatible with your PC." ); } private static String unsupportedSystemMessage() { final String osArch = System.getProperty("os.arch"); final String osName = System.getProperty("os.name"); return String.format( "Failed loading native library. Your CPU is %s and your operating system is %s. " + "Supported architectures are 64 bit x86, and 64 bit ARM. Supported operating systems are Windows, " + "Linux, and Mac", osArch, osName ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ExecutionControlCommands.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.process.IBaritoneProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; /** * Contains the pause, resume, and paused commands. * <p> * This thing is scoped to hell, private so far you can't even access it using reflection, because you AREN'T SUPPOSED * TO USE THIS to pause and resume Baritone. Make your own process that returns {@link PathingCommandType#REQUEST_PAUSE * REQUEST_PAUSE} as needed. */ public class ExecutionControlCommands { Command pauseCommand; Command resumeCommand; Command pausedCommand; Command cancelCommand; public ExecutionControlCommands(IBaritone baritone) { // array for mutability, non-field so reflection can't touch it final boolean[] paused = {false}; baritone.getPathingControlManager().registerProcess( new IBaritoneProcess() { @Override public boolean isActive() { return paused[0]; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { baritone.getInputOverrideHandler().clearAllKeys(); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } @Override public boolean isTemporary() { return true; } @Override public void onLostControl() { } @Override public double priority() { return DEFAULT_PRIORITY + 1; } @Override public String displayName0() { return "Pause/Resume Commands"; } } ); pauseCommand = new Command(baritone, "pause", "p", "paws") { @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); if (paused[0]) { throw new CommandInvalidStateException("Already paused"); } paused[0] = true; logDirect("Paused"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Pauses Baritone until you use resume"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The pause command tells Baritone to temporarily stop whatever it's doing.", "", "This can be used to pause pathing, building, following, whatever. A single use of the resume command will start it right back up again!", "", "Usage:", "> pause" ); } }; resumeCommand = new Command(baritone, "resume", "r", "unpause", "unpaws") { @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); baritone.getBuilderProcess().resume(); if (!paused[0]) { throw new CommandInvalidStateException("Not paused"); } paused[0] = false; logDirect("Resumed"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Resumes Baritone after a pause"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The resume command tells Baritone to resume whatever it was doing when you last used pause.", "", "Usage:", "> resume" ); } }; pausedCommand = new Command(baritone, "paused") { @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); logDirect(String.format("Baritone is %spaused", paused[0] ? "" : "not ")); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Tells you if Baritone is paused"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The paused command tells you if Baritone is currently paused by use of the pause command.", "", "Usage:", "> paused" ); } }; cancelCommand = new Command(baritone, "cancel", "c", "stop") { @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); if (paused[0]) { paused[0] = false; } baritone.getPathingBehavior().cancelEverything(); logDirect("ok canceled"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Cancel what Baritone is currently doing"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The cancel command tells Baritone to stop whatever it's currently doing.", "", "Usage:", "> cancel" ); } }; } } ================================================ FILE: src/main/java/baritone/command/defaults/ExploreCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.RelativeGoalXZ; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalXZ; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ExploreCommand extends Command { public ExploreCommand(IBaritone baritone) { super(baritone, "explore"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { if (args.hasAny()) { args.requireExactly(2); } else { args.requireMax(0); } GoalXZ goal = args.hasAny() ? args.getDatatypePost(RelativeGoalXZ.INSTANCE, ctx.playerFeet()) : new GoalXZ(ctx.playerFeet()); baritone.getExploreProcess().explore(goal.getX(), goal.getZ()); logDirect(String.format("Exploring from %s", goal.toString())); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { if (args.hasAtMost(2)) { return args.tabCompleteDatatype(RelativeGoalXZ.INSTANCE); } return Stream.empty(); } @Override public String getShortDesc() { return "Explore things"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Tell Baritone to explore randomly. If you used explorefilter before this, it will be applied.", "", "Usage:", "> explore - Explore from your current position.", "> explore <x> <z> - Explore from the specified X and Z position." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ExploreFilterCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.RelativeFile; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.command.exception.CommandInvalidTypeException; import com.google.gson.JsonSyntaxException; import java.io.File; import java.nio.file.NoSuchFileException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ExploreFilterCommand extends Command { public ExploreFilterCommand(IBaritone baritone) { super(baritone, "explorefilter"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(2); File file = args.getDatatypePost(RelativeFile.INSTANCE, ctx.minecraft().gameDirectory.getAbsoluteFile().getParentFile()); boolean invert = false; if (args.hasAny()) { if (args.getString().equalsIgnoreCase("invert")) { invert = true; } else { throw new CommandInvalidTypeException(args.consumed(), "either \"invert\" or nothing"); } } try { baritone.getExploreProcess().applyJsonFilter(file.toPath().toAbsolutePath(), invert); } catch (NoSuchFileException e) { throw new CommandInvalidStateException("File not found"); } catch (JsonSyntaxException e) { throw new CommandInvalidStateException("Invalid JSON syntax"); } catch (Exception e) { throw new IllegalStateException(e); } logDirect(String.format("Explore filter applied. Inverted: %s", Boolean.toString(invert))); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasExactlyOne()) { return RelativeFile.tabComplete(args, RelativeFile.gameDir(ctx.minecraft())); } return Stream.empty(); } @Override public String getShortDesc() { return "Explore chunks from a json"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Apply an explore filter before using explore, which tells the explore process which chunks have been explored/not explored.", "", "The JSON file will follow this format: [{\"x\":0,\"z\":0},...]", "", "If 'invert' is specified, the chunks listed will be considered NOT explored, rather than explored.", "", "Usage:", "> explorefilter <path> [invert] - Load the JSON file referenced by the specified path. If invert is specified, it must be the literal word 'invert'." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/FarmCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.cache.IWaypoint; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.ForWaypoints; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.utils.BetterBlockPos; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class FarmCommand extends Command { public FarmCommand(IBaritone baritone) { super(baritone, "farm"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(2); int range = 0; BetterBlockPos origin = null; //range if (args.has(1)) { range = args.getAs(Integer.class); } //waypoint if (args.has(1)) { IWaypoint[] waypoints = args.getDatatypeFor(ForWaypoints.INSTANCE); IWaypoint waypoint = null; switch (waypoints.length) { case 0: throw new CommandInvalidStateException("No waypoints found"); case 1: waypoint = waypoints[0]; break; default: throw new CommandInvalidStateException("Multiple waypoints were found"); } origin = waypoint.getLocation(); } baritone.getFarmProcess().farm(range, origin); logDirect("Farming"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Farm nearby crops"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The farm command starts farming nearby plants. It harvests mature crops and plants new ones.", "", "Usage:", "> farm - farms every crop it can find.", "> farm <range> - farm crops within range from the starting position.", "> farm <range> <waypoint> - farm crops within range from waypoint." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/FindCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.BlockById; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.utils.BetterBlockPos; import baritone.cache.CachedChunk; import net.minecraft.core.Registry; import net.minecraft.ChatFormatting; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import net.minecraft.world.level.block.Block; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class FindCommand extends Command { public FindCommand(IBaritone baritone) { super(baritone, "find"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMin(1); List<Block> toFind = new ArrayList<>(); while (args.hasAny()) { toFind.add(args.getDatatypeFor(BlockById.INSTANCE)); } BetterBlockPos origin = ctx.playerFeet(); Component[] components = toFind.stream() .flatMap(block -> ctx.worldData().getCachedWorld().getLocationsOf( BuiltInRegistries.BLOCK.getKey(block).getPath(), Integer.MAX_VALUE, origin.x, origin.y, 4 ).stream() ) .map(BetterBlockPos::new) .map(this::positionToComponent) .toArray(Component[]::new); if (components.length > 0) { Arrays.asList(components).forEach(this::logDirect); } else { logDirect("No positions known, are you sure the blocks are cached?"); } } private Component positionToComponent(BetterBlockPos pos) { String positionText = String.format("%s %s %s", pos.x, pos.y, pos.z); String command = String.format("%sgoal %s", FORCE_COMMAND_PREFIX, positionText); MutableComponent baseComponent = Component.literal(pos.toString()); MutableComponent hoverComponent = Component.literal("Click to set goal to this position"); baseComponent.setStyle(baseComponent.getStyle() .withColor(ChatFormatting.GRAY) .withInsertion(positionText) .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command)) .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponent))); return baseComponent; } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { return new TabCompleteHelper() .append( CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.stream() .map(BuiltInRegistries.BLOCK::getKey) .map(Object::toString) ) .filterPrefixNamespaced(args.getString()) .sortAlphabetically() .stream(); } @Override public String getShortDesc() { return "Find positions of a certain block"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The find command searches through Baritone's cache and attempts to find the location of the block.", "Tab completion will suggest only cached blocks and uncached blocks can not be found.", "", "Usage:", "> find <block> [...] - Try finding the listed blocks" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/FollowCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.KeepName; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.EntityClassById; import baritone.api.command.datatypes.IDatatypeFor; import baritone.api.command.datatypes.NearbyPlayer; import baritone.api.command.exception.CommandErrorMessageException; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import java.util.*; import java.util.function.Predicate; import java.util.stream.Stream; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; public class FollowCommand extends Command { public FollowCommand(IBaritone baritone) { super(baritone, "follow"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMin(1); FollowGroup group; FollowList list; List<Entity> entities = new ArrayList<>(); List<EntityType> classes = new ArrayList<>(); if (args.hasExactlyOne()) { baritone.getFollowProcess().follow((group = args.getEnum(FollowGroup.class)).filter); } else { args.requireMin(2); group = null; list = args.getEnum(FollowList.class); while (args.hasAny()) { Object gotten = args.getDatatypeFor(list.datatype); if (gotten instanceof EntityType) { //noinspection unchecked classes.add((EntityType) gotten); } else if (gotten != null) { entities.add((Entity) gotten); } } baritone.getFollowProcess().follow( classes.isEmpty() ? entities::contains : e -> classes.stream().anyMatch(c -> e.getType().equals(c)) ); } if (group != null) { logDirect(String.format("Following all %s", group.name().toLowerCase(Locale.US))); } else { if (classes.isEmpty()) { if (entities.isEmpty()) throw new NoEntitiesException(); logDirect("Following these entities:"); entities.stream() .map(Entity::toString) .forEach(this::logDirect); } else { logDirect("Following these types of entities:"); classes.stream() .map(BuiltInRegistries.ENTITY_TYPE::getKey) .map(Objects::requireNonNull) .map(ResourceLocation::toString) .forEach(this::logDirect); } } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasExactlyOne()) { return new TabCompleteHelper() .append(FollowGroup.class) .append(FollowList.class) .filterPrefix(args.getString()) .stream(); } else { IDatatypeFor followType; try { followType = args.getEnum(FollowList.class).datatype; } catch (NullPointerException e) { return Stream.empty(); } while (args.has(2)) { if (args.peekDatatypeOrNull(followType) == null) { return Stream.empty(); } args.get(); } return args.tabCompleteDatatype(followType); } } @Override public String getShortDesc() { return "Follow entity things"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The follow command tells Baritone to follow certain kinds of entities.", "", "Usage:", "> follow entities - Follows all entities.", "> follow entity <entity1> <entity2> <...> - Follow certain entities (for example 'skeleton', 'horse' etc.)", "> follow players - Follow players", "> follow player <username1> <username2> <...> - Follow certain players" ); } @KeepName private enum FollowGroup { ENTITIES(LivingEntity.class::isInstance), PLAYERS(Player.class::isInstance); /* , FRIENDLY(entity -> entity.getAttackTarget() != HELPER.mc.player), HOSTILE(FRIENDLY.filter.negate()); */ final Predicate<Entity> filter; FollowGroup(Predicate<Entity> filter) { this.filter = filter; } } @KeepName private enum FollowList { ENTITY(EntityClassById.INSTANCE), PLAYER(NearbyPlayer.INSTANCE); final IDatatypeFor datatype; FollowList(IDatatypeFor datatype) { this.datatype = datatype; } } public static class NoEntitiesException extends CommandErrorMessageException { protected NoEntitiesException() { super("No valid entities in range!"); } } } ================================================ FILE: src/main/java/baritone/command/defaults/ForceCancelCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.behavior.IPathingBehavior; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ForceCancelCommand extends Command { public ForceCancelCommand(IBaritone baritone) { super(baritone, "forcecancel"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); IPathingBehavior pathingBehavior = baritone.getPathingBehavior(); pathingBehavior.cancelEverything(); pathingBehavior.forceCancel(); logDirect("ok force canceled"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Force cancel"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Like cancel, but more forceful.", "", "Usage:", "> forcecancel" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/GcCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class GcCommand extends Command { public GcCommand(IBaritone baritone) { super(baritone, "gc"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); System.gc(); logDirect("ok called System.gc()"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Call System.gc()"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Calls System.gc().", "", "Usage:", "> gc" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/GoalCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.RelativeCoordinate; import baritone.api.command.datatypes.RelativeGoal; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.pathing.goals.Goal; import baritone.api.process.ICustomGoalProcess; import baritone.api.utils.BetterBlockPos; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class GoalCommand extends Command { public GoalCommand(IBaritone baritone) { super(baritone, "goal"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { ICustomGoalProcess goalProcess = baritone.getCustomGoalProcess(); if (args.hasAny() && Arrays.asList("reset", "clear", "none").contains(args.peekString())) { args.requireMax(1); if (goalProcess.getGoal() != null) { goalProcess.setGoal(null); logDirect("Cleared goal"); } else { logDirect("There was no goal to clear"); } } else { args.requireMax(3); BetterBlockPos origin = ctx.playerFeet(); Goal goal = args.getDatatypePost(RelativeGoal.INSTANCE, origin); goalProcess.setGoal(goal); logDirect(String.format("Goal: %s", goal.toString())); } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { TabCompleteHelper helper = new TabCompleteHelper(); if (args.hasExactlyOne()) { helper.append("reset", "clear", "none", "~"); } else { if (args.hasAtMost(3)) { while (args.has(2)) { if (args.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) { break; } args.get(); if (!args.has(2)) { helper.append("~"); } } } } return helper.filterPrefix(args.getString()).stream(); } @Override public String getShortDesc() { return "Set or clear the goal"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The goal command allows you to set or clear Baritone's goal.", "", "Wherever a coordinate is expected, you can use ~ just like in regular Minecraft commands. Or, you can just use regular numbers.", "", "Usage:", "> goal - Set the goal to your current position", "> goal <reset/clear/none> - Erase the goal", "> goal <y> - Set the goal to a Y level", "> goal <x> <z> - Set the goal to an X,Z position", "> goal <x> <y> <z> - Set the goal to an X,Y,Z position" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/GotoCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.ForBlockOptionalMeta; import baritone.api.command.datatypes.RelativeCoordinate; import baritone.api.command.datatypes.RelativeGoal; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.Goal; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BlockOptionalMeta; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class GotoCommand extends Command { protected GotoCommand(IBaritone baritone) { super(baritone, "goto"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { // If we have a numeric first argument, then parse arguments as coordinates. // Note: There is no reason to want to go where you're already at so there // is no need to handle the case of empty arguments. if (args.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) != null) { args.requireMax(3); BetterBlockPos origin = ctx.playerFeet(); Goal goal = args.getDatatypePost(RelativeGoal.INSTANCE, origin); logDirect(String.format("Going to: %s", goal.toString())); baritone.getCustomGoalProcess().setGoalAndPath(goal); return; } args.requireMax(1); BlockOptionalMeta destination = args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); baritone.getGetToBlockProcess().getToBlock(destination); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { // since it's either a goal or a block, I don't think we can tab complete properly? // so just tab complete for the block variant args.requireMax(1); return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); } @Override public String getShortDesc() { return "Go to a coordinate or block"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The goto command tells Baritone to head towards a given goal or block.", "", "Wherever a coordinate is expected, you can use ~ just like in regular Minecraft commands. Or, you can just use regular numbers.", "", "Usage:", "> goto <block> - Go to a block, wherever it is in the world", "> goto <y> - Go to a Y level", "> goto <x> <z> - Go to an X,Z position", "> goto <x> <y> <z> - Go to an X,Y,Z position" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/HelpCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.ICommand; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandNotFoundException; import baritone.api.command.helpers.Paginator; import baritone.api.command.helpers.TabCompleteHelper; import java.awt.*; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class HelpCommand extends Command { public HelpCommand(IBaritone baritone) { super(baritone, "help", "?"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(1); if (!args.hasAny() || args.is(Integer.class)) { Paginator.paginate( args, new Paginator<>( this.baritone.getCommandManager().getRegistry().descendingStream() .filter(command -> !command.hiddenFromHelp()) .collect(Collectors.toList()) ), () -> logDirect("All Baritone commands (clickable):"), command -> { String names = String.join("/", command.getNames()); String name = command.getNames().get(0); MutableComponent shortDescComponent = Component.literal(" - " + command.getShortDesc()); shortDescComponent.setStyle(shortDescComponent.getStyle().withColor(ChatFormatting.DARK_GRAY)); MutableComponent namesComponent = Component.literal(names); namesComponent.setStyle(namesComponent.getStyle().withColor(ChatFormatting.WHITE)); MutableComponent hoverComponent = Component.literal(""); hoverComponent.setStyle(hoverComponent.getStyle().withColor(ChatFormatting.GRAY)); hoverComponent.append(namesComponent); hoverComponent.append("\n" + command.getShortDesc()); hoverComponent.append("\n\nClick to view full help"); String clickCommand = FORCE_COMMAND_PREFIX + String.format("%s %s", label, command.getNames().get(0)); MutableComponent component = Component.literal(name); component.setStyle(component.getStyle().withColor(ChatFormatting.GRAY)); component.append(shortDescComponent); component.setStyle(component.getStyle() .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponent)) .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, clickCommand))); return component; }, FORCE_COMMAND_PREFIX + label ); } else { String commandName = args.getString().toLowerCase(); ICommand command = this.baritone.getCommandManager().getCommand(commandName); if (command == null) { throw new CommandNotFoundException(commandName); } logDirect(String.format("%s - %s", String.join(" / ", command.getNames()), command.getShortDesc())); logDirect(""); command.getLongDesc().forEach(this::logDirect); logDirect(""); MutableComponent returnComponent = Component.literal("Click to return to the help menu"); returnComponent.setStyle(returnComponent.getStyle().withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + label ))); logDirect(returnComponent); } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasExactlyOne()) { return new TabCompleteHelper() .addCommands(this.baritone.getCommandManager()) .filterPrefix(args.getString()) .stream(); } return Stream.empty(); } @Override public String getShortDesc() { return "View all commands or help on specific ones"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Using this command, you can view detailed help information on how to use certain commands of Baritone.", "", "Usage:", "> help - Lists all commands and their short descriptions.", "> help <command> - Displays help information on a specific command." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/InvertCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalInverted; import baritone.api.process.ICustomGoalProcess; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class InvertCommand extends Command { public InvertCommand(IBaritone baritone) { super(baritone, "invert"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); Goal goal; if ((goal = customGoalProcess.getGoal()) == null) { throw new CommandInvalidStateException("No goal"); } if (goal instanceof GoalInverted) { goal = ((GoalInverted) goal).origin; } else { goal = new GoalInverted(goal); } customGoalProcess.setGoalAndPath(goal); logDirect(String.format("Goal: %s", goal.toString())); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Run away from the current goal"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The invert command tells Baritone to head away from the current goal rather than towards it.", "", "Usage:", "> invert - Invert the current goal." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/LitematicaCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class LitematicaCommand extends Command { public LitematicaCommand(IBaritone baritone) { super(baritone, "litematica"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(1); int schematic = args.hasAny() ? args.getAs(Integer.class) - 1 : 0; baritone.getBuilderProcess().buildOpenLitematic(schematic); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Builds the loaded schematic"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Build a schematic currently open in Litematica.", "", "Usage:", "> litematica", "> litematica <#>" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/MineCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.ForBlockOptionalMeta; import baritone.api.command.exception.CommandException; import baritone.api.utils.BlockOptionalMeta; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class MineCommand extends Command { public MineCommand(IBaritone baritone) { super(baritone, "mine"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { int quantity = args.getAsOrDefault(Integer.class, 0); args.requireMin(1); List<BlockOptionalMeta> boms = new ArrayList<>(); while (args.hasAny()) { boms.add(args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE)); } BaritoneAPI.getProvider().getWorldScanner().repack(ctx); logDirect(String.format("Mining %s", boms.toString())); baritone.getMineProcess().mine(quantity, boms.toArray(new BlockOptionalMeta[0])); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { args.getAsOrDefault(Integer.class, 0); while (args.has(2)) { args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); } return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); } @Override public String getShortDesc() { return "Mine some blocks"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The mine command allows you to tell Baritone to search for and mine individual blocks.", "", "The specified blocks can be ores, or any other block.", "", "Also see the legitMine settings (see #set l legitMine).", "", "Usage:", "> mine diamond_ore - Mines all diamonds it can find." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/PathCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.process.ICustomGoalProcess; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class PathCommand extends Command { public PathCommand(IBaritone baritone) { super(baritone, "path"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); args.requireMax(0); BaritoneAPI.getProvider().getWorldScanner().repack(ctx); customGoalProcess.path(); logDirect("Now pathing"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { return Stream.empty(); } @Override public String getShortDesc() { return "Start heading towards the goal"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The path command tells Baritone to head towards the current goal.", "", "Usage:", "> path - Start the pathing." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/PickupCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.ItemById; import baritone.api.command.exception.CommandException; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Stream; public class PickupCommand extends Command { public PickupCommand(IBaritone baritone) { super(baritone, "pickup"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { Set<Item> collecting = new HashSet<>(); while (args.hasAny()) { Item item = args.getDatatypeFor(ItemById.INSTANCE); collecting.add(item); } if (collecting.isEmpty()) { baritone.getFollowProcess().pickup(stack -> true); logDirect("Picking up all items"); } else { baritone.getFollowProcess().pickup(stack -> collecting.contains(stack.getItem())); logDirect("Picking up these items:"); collecting.stream().map(BuiltInRegistries.ITEM::getKey).map(ResourceLocation::toString).forEach(this::logDirect); } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { while (args.has(2)) { if (args.peekDatatypeOrNull(ItemById.INSTANCE) == null) { return Stream.empty(); } args.get(); } return args.tabCompleteDatatype(ItemById.INSTANCE); } @Override public String getShortDesc() { return "Pickup items"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Usage:", "> pickup - Pickup anything", "> pickup <item1> <item2> <...> - Pickup certain items" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ProcCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.pathing.calc.IPathingControlManager; import baritone.api.process.IBaritoneProcess; import baritone.api.process.PathingCommand; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ProcCommand extends Command { public ProcCommand(IBaritone baritone) { super(baritone, "proc"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); IPathingControlManager pathingControlManager = baritone.getPathingControlManager(); IBaritoneProcess process = pathingControlManager.mostRecentInControl().orElse(null); if (process == null) { throw new CommandInvalidStateException("No process in control"); } logDirect(String.format( "Class: %s\n" + "Priority: %f\n" + "Temporary: %b\n" + "Display name: %s\n" + "Last command: %s", process.getClass().getTypeName(), process.priority(), process.isTemporary(), process.displayName(), pathingControlManager .mostRecentCommand() .map(PathingCommand::toString) .orElse("None") )); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "View process state information"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The proc command provides miscellaneous information about the process currently controlling Baritone.", "", "You are not expected to understand this if you aren't familiar with how Baritone works.", "", "Usage:", "> proc - View process information, if present" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ReloadAllCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ReloadAllCommand extends Command { public ReloadAllCommand(IBaritone baritone) { super(baritone, "reloadall"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); ctx.worldData().getCachedWorld().reloadAllFromDisk(); logDirect("Reloaded"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Reloads Baritone's cache for this world"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The reloadall command reloads Baritone's world cache.", "", "Usage:", "> reloadall" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/RenderCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.utils.BetterBlockPos; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class RenderCommand extends Command { public RenderCommand(IBaritone baritone) { super(baritone, "render"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); BetterBlockPos origin = ctx.playerFeet(); int renderDistance = (ctx.minecraft().options.renderDistance().get() + 1) * 16; ctx.minecraft().levelRenderer.setBlocksDirty( origin.x - renderDistance, ctx.world().getMinBuildHeight(), origin.z - renderDistance, origin.x + renderDistance, ctx.world().getMaxBuildHeight(), origin.z + renderDistance ); logDirect("Done"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Fix glitched chunks"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The render command fixes glitched chunk rendering without having to reload all of them.", "", "Usage:", "> render" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/RepackCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class RepackCommand extends Command { public RepackCommand(IBaritone baritone) { super(baritone, "repack", "rescan"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); logDirect(String.format("Queued %d chunks for repacking", BaritoneAPI.getProvider().getWorldScanner().repack(ctx))); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Re-cache chunks"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Repack chunks around you. This basically re-caches them.", "", "Usage:", "> repack - Repack chunks." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/SaveAllCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class SaveAllCommand extends Command { public SaveAllCommand(IBaritone baritone) { super(baritone, "saveall"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); ctx.worldData().getCachedWorld().save(); logDirect("Saved"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Saves Baritone's cache for this world"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The saveall command saves Baritone's world cache.", "", "Usage:", "> saveall" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/SchematicaCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class SchematicaCommand extends Command { public SchematicaCommand(IBaritone baritone) { super(baritone, "schematica"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); baritone.getBuilderProcess().buildOpenSchematic(); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Builds the loaded schematic"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Builds the schematic currently open in Schematica.", "", "Usage:", "> schematica" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/SelCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.ForAxis; import baritone.api.command.datatypes.ForBlockOptionalMeta; import baritone.api.command.datatypes.ForDirection; import baritone.api.command.datatypes.RelativeBlockPos; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.event.events.RenderEvent; import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.schematic.*; import baritone.api.schematic.mask.shape.CylinderMask; import baritone.api.schematic.mask.shape.SphereMask; import baritone.api.selection.ISelection; import baritone.api.selection.ISelectionManager; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BlockOptionalMeta; import baritone.api.utils.BlockOptionalMetaLookup; import baritone.utils.BlockStateInterface; import baritone.utils.IRenderer; import baritone.utils.schematic.StaticSchematic; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import java.awt.*; import java.util.*; import java.util.List; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.Stream; public class SelCommand extends Command { private ISelectionManager manager = baritone.getSelectionManager(); private BetterBlockPos pos1 = null; private ISchematic clipboard = null; private Vec3i clipboardOffset = null; public SelCommand(IBaritone baritone) { super(baritone, "sel", "selection", "s"); baritone.getGameEventHandler().registerEventListener(new AbstractGameEventListener() { @Override public void onRenderPass(RenderEvent event) { if (!Baritone.settings().renderSelectionCorners.value || pos1 == null) { return; } Color color = Baritone.settings().colorSelectionPos1.value; float opacity = Baritone.settings().selectionOpacity.value; float lineWidth = Baritone.settings().selectionLineWidth.value; boolean ignoreDepth = Baritone.settings().renderSelectionIgnoreDepth.value; IRenderer.startLines(color, opacity, lineWidth, ignoreDepth); IRenderer.emitAABB(event.getModelViewStack(), new AABB(pos1, pos1.offset(1, 1, 1))); IRenderer.endLines(ignoreDepth); } }); } @Override public void execute(String label, IArgConsumer args) throws CommandException { Action action = Action.getByName(args.getString()); if (action == null) { throw new CommandInvalidTypeException(args.consumed(), "an action"); } if (action == Action.POS1 || action == Action.POS2) { if (action == Action.POS2 && pos1 == null) { throw new CommandInvalidStateException("Set pos1 first before using pos2"); } BetterBlockPos playerPos = ctx.viewerPos(); BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, playerPos) : playerPos; args.requireMax(0); if (action == Action.POS1) { pos1 = pos; logDirect("Position 1 has been set"); } else { manager.addSelection(pos1, pos); pos1 = null; logDirect("Selection added"); } } else if (action == Action.CLEAR) { args.requireMax(0); pos1 = null; logDirect(String.format("Removed %d selections", manager.removeAllSelections().length)); } else if (action == Action.UNDO) { args.requireMax(0); if (pos1 != null) { pos1 = null; logDirect("Undid pos1"); } else { ISelection[] selections = manager.getSelections(); if (selections.length < 1) { throw new CommandInvalidStateException("Nothing to undo!"); } else { pos1 = manager.removeSelection(selections[selections.length - 1]).pos1(); logDirect("Undid pos2"); } } } else if (action.isFillAction()) { BlockOptionalMeta type = action == Action.CLEARAREA ? new BlockOptionalMeta(Blocks.AIR) : args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); final BlockOptionalMetaLookup replaces; // Action.REPLACE final Direction.Axis alignment; // Action.(H)CYLINDER if (action == Action.REPLACE) { args.requireMin(1); List<BlockOptionalMeta> replacesList = new ArrayList<>(); replacesList.add(type); while (args.has(2)) { replacesList.add(args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE)); } type = args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); replaces = new BlockOptionalMetaLookup(replacesList.toArray(new BlockOptionalMeta[0])); alignment = null; } else if (action == Action.CYLINDER || action == Action.HCYLINDER) { args.requireMax(1); alignment = args.hasAny() ? args.getDatatypeFor(ForAxis.INSTANCE) : Direction.Axis.Y; replaces = null; } else { args.requireMax(0); replaces = null; alignment = null; } ISelection[] selections = manager.getSelections(); if (selections.length == 0) { throw new CommandInvalidStateException("No selections"); } BetterBlockPos origin = selections[0].min(); CompositeSchematic composite = new CompositeSchematic(0, 0, 0); for (ISelection selection : selections) { BetterBlockPos min = selection.min(); origin = new BetterBlockPos( Math.min(origin.x, min.x), Math.min(origin.y, min.y), Math.min(origin.z, min.z) ); } for (ISelection selection : selections) { Vec3i size = selection.size(); BetterBlockPos min = selection.min(); // Java 8 so no switch expressions 😿 UnaryOperator<ISchematic> create = fill -> { final int w = fill.widthX(); final int h = fill.heightY(); final int l = fill.lengthZ(); switch (action) { case WALLS: return new WallsSchematic(fill); case SHELL: return new ShellSchematic(fill); case REPLACE: return new ReplaceSchematic(fill, replaces); case SPHERE: return MaskSchematic.create(fill, new SphereMask(w, h, l, true).compute()); case HSPHERE: return MaskSchematic.create(fill, new SphereMask(w, h, l, false).compute()); case CYLINDER: return MaskSchematic.create(fill, new CylinderMask(w, h, l, true, alignment).compute()); case HCYLINDER: return MaskSchematic.create(fill, new CylinderMask(w, h, l, false, alignment).compute()); default: // Silent fail return fill; } }; ISchematic schematic = create.apply(new FillSchematic(size.getX(), size.getY(), size.getZ(), type)); composite.put(schematic, min.x - origin.x, min.y - origin.y, min.z - origin.z); } baritone.getBuilderProcess().build("Fill", composite, origin); logDirect("Filling now"); } else if (action == Action.COPY) { BetterBlockPos playerPos = ctx.viewerPos(); BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, playerPos) : playerPos; args.requireMax(0); ISelection[] selections = manager.getSelections(); if (selections.length < 1) { throw new CommandInvalidStateException("No selections"); } BlockStateInterface bsi = new BlockStateInterface(ctx); BetterBlockPos origin = selections[0].min(); CompositeSchematic composite = new CompositeSchematic(0, 0, 0); for (ISelection selection : selections) { BetterBlockPos min = selection.min(); origin = new BetterBlockPos( Math.min(origin.x, min.x), Math.min(origin.y, min.y), Math.min(origin.z, min.z) ); } for (ISelection selection : selections) { Vec3i size = selection.size(); BetterBlockPos min = selection.min(); BlockState[][][] blockstates = new BlockState[size.getX()][size.getZ()][size.getY()]; for (int x = 0; x < size.getX(); x++) { for (int y = 0; y < size.getY(); y++) { for (int z = 0; z < size.getZ(); z++) { blockstates[x][z][y] = bsi.get0(min.x + x, min.y + y, min.z + z); } } } ISchematic schematic = new StaticSchematic(blockstates); composite.put(schematic, min.x - origin.x, min.y - origin.y, min.z - origin.z); } clipboard = composite; clipboardOffset = origin.subtract(pos); logDirect("Selection copied"); } else if (action == Action.PASTE) { BetterBlockPos playerPos = ctx.viewerPos(); BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, playerPos) : playerPos; args.requireMax(0); if (clipboard == null) { throw new CommandInvalidStateException("You need to copy a selection first"); } baritone.getBuilderProcess().build("Fill", clipboard, pos.offset(clipboardOffset)); logDirect("Building now"); } else if (action == Action.EXPAND || action == Action.CONTRACT || action == Action.SHIFT) { args.requireExactly(3); TransformTarget transformTarget = TransformTarget.getByName(args.getString()); if (transformTarget == null) { throw new CommandInvalidStateException("Invalid transform type"); } Direction direction = args.getDatatypeFor(ForDirection.INSTANCE); int blocks = args.getAs(Integer.class); ISelection[] selections = manager.getSelections(); if (selections.length < 1) { throw new CommandInvalidStateException("No selections found"); } selections = transformTarget.transform(selections); for (ISelection selection : selections) { if (action == Action.EXPAND) { manager.expand(selection, direction, blocks); } else if (action == Action.CONTRACT) { manager.contract(selection, direction, blocks); } else { manager.shift(selection, direction, blocks); } } logDirect(String.format("Transformed %d selections", selections.length)); } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasExactlyOne()) { return new TabCompleteHelper() .append(Action.getAllNames()) .filterPrefix(args.getString()) .sortAlphabetically() .stream(); } else { Action action = Action.getByName(args.getString()); if (action != null) { if (action == Action.POS1 || action == Action.POS2) { if (args.hasAtMost(3)) { return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); } } else if (action.isFillAction()) { if (args.hasExactlyOne() || action == Action.REPLACE) { while (args.has(2)) { args.get(); } return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); } else if (args.hasExactly(2) && (action == Action.CYLINDER || action == Action.HCYLINDER)) { args.get(); return args.tabCompleteDatatype(ForAxis.INSTANCE); } } else if (action == Action.EXPAND || action == Action.CONTRACT || action == Action.SHIFT) { if (args.hasExactlyOne()) { return new TabCompleteHelper() .append(TransformTarget.getAllNames()) .filterPrefix(args.getString()) .sortAlphabetically() .stream(); } else { TransformTarget target = TransformTarget.getByName(args.getString()); if (target != null && args.hasExactlyOne()) { return args.tabCompleteDatatype(ForDirection.INSTANCE); } } } } } return Stream.empty(); } @Override public String getShortDesc() { return "WorldEdit-like commands"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The sel command allows you to manipulate Baritone's selections, similarly to WorldEdit.", "", "Using these selections, you can clear areas, fill them with blocks, or something else.", "", "The expand/contract/shift commands use a kind of selector to choose which selections to target. Supported ones are a/all, n/newest, and o/oldest.", "", "Usage:", "> sel pos1/p1/1 - Set position 1 to your current position.", "> sel pos1/p1/1 <x> <y> <z> - Set position 1 to a relative position.", "> sel pos2/p2/2 - Set position 2 to your current position.", "> sel pos2/p2/2 <x> <y> <z> - Set position 2 to a relative position.", "", "> sel clear/c - Clear the selection.", "> sel undo/u - Undo the last action (setting positions, creating selections, etc.)", "> sel set/fill/s/f [block] - Completely fill all selections with a block.", "> sel walls/w [block] - Fill in the walls of the selection with a specified block.", "> sel shell/shl [block] - The same as walls, but fills in a ceiling and floor too.", "> sel sphere/sph [block] - Fills the selection with a sphere bounded by the sides.", "> sel hsphere/hsph [block] - The same as sphere, but hollow.", "> sel cylinder/cyl [block] <axis> - Fills the selection with a cylinder bounded by the sides, oriented about the given axis. (default=y)", "> sel hcylinder/hcyl [block] <axis> - The same as cylinder, but hollow.", "> sel cleararea/ca - Basically 'set air'.", "> sel replace/r <blocks...> <with> - Replaces blocks with another block.", "> sel copy/cp <x> <y> <z> - Copy the selected area relative to the specified or your position.", "> sel paste/p <x> <y> <z> - Build the copied area relative to the specified or your position.", "", "> sel expand <target> <direction> <blocks> - Expand the targets.", "> sel contract <target> <direction> <blocks> - Contract the targets.", "> sel shift <target> <direction> <blocks> - Shift the targets (does not resize)." ); } enum Action { POS1("pos1", "p1", "1"), POS2("pos2", "p2", "2"), CLEAR("clear", "c"), UNDO("undo", "u"), SET("set", "fill", "s", "f"), WALLS("walls", "w"), SHELL("shell", "shl"), SPHERE("sphere", "sph"), HSPHERE("hsphere", "hsph"), CYLINDER("cylinder", "cyl"), HCYLINDER("hcylinder", "hcyl"), CLEARAREA("cleararea", "ca"), REPLACE("replace", "r"), EXPAND("expand", "ex"), COPY("copy", "cp"), PASTE("paste", "p"), CONTRACT("contract", "ct"), SHIFT("shift", "sh"); private final String[] names; Action(String... names) { this.names = names; } public static Action getByName(String name) { for (Action action : Action.values()) { for (String alias : action.names) { if (alias.equalsIgnoreCase(name)) { return action; } } } return null; } public static String[] getAllNames() { Set<String> names = new HashSet<>(); for (Action action : Action.values()) { names.addAll(Arrays.asList(action.names)); } return names.toArray(new String[0]); } public final boolean isFillAction() { return this == SET || this == WALLS || this == SHELL || this == SPHERE || this == HSPHERE || this == CYLINDER || this == HCYLINDER || this == CLEARAREA || this == REPLACE; } } enum TransformTarget { ALL(sels -> sels, "all", "a"), NEWEST(sels -> new ISelection[]{sels[sels.length - 1]}, "newest", "n"), OLDEST(sels -> new ISelection[]{sels[0]}, "oldest", "o"); private final Function<ISelection[], ISelection[]> transform; private final String[] names; TransformTarget(Function<ISelection[], ISelection[]> transform, String... names) { this.transform = transform; this.names = names; } public ISelection[] transform(ISelection[] selections) { return transform.apply(selections); } public static TransformTarget getByName(String name) { for (TransformTarget target : TransformTarget.values()) { for (String alias : target.names) { if (alias.equalsIgnoreCase(name)) { return target; } } } return null; } public static String[] getAllNames() { Set<String> names = new HashSet<>(); for (TransformTarget target : TransformTarget.values()) { names.addAll(Arrays.asList(target.names)); } return names.toArray(new String[0]); } } } ================================================ FILE: src/main/java/baritone/command/defaults/SetCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.Settings; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.RelativeFile; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.helpers.Paginator; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.utils.SettingsUtil; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; import java.util.stream.Stream; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; import static baritone.api.utils.SettingsUtil.*; public class SetCommand extends Command { public SetCommand(IBaritone baritone) { super(baritone, "set", "setting", "settings"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { String arg = args.hasAny() ? args.getString().toLowerCase(Locale.US) : "list"; if (Arrays.asList("s", "save").contains(arg)) { SettingsUtil.save(Baritone.settings()); logDirect("Settings saved"); return; } if (Arrays.asList("load", "ld").contains(arg)) { String file = SETTINGS_DEFAULT_NAME; if (args.hasAny()) { file = args.getString(); } // reset to defaults SettingsUtil.modifiedSettings(Baritone.settings()).forEach(Settings.Setting::reset); // then load from disk SettingsUtil.readAndApply(Baritone.settings(), file); logDirect("Settings reloaded from " + file); return; } boolean viewModified = Arrays.asList("m", "mod", "modified").contains(arg); boolean viewAll = Arrays.asList("all", "l", "list").contains(arg); boolean paginate = viewModified || viewAll; if (paginate) { String search = args.hasAny() && args.peekAsOrNull(Integer.class) == null ? args.getString() : ""; args.requireMax(1); List<? extends Settings.Setting> toPaginate = (viewModified ? SettingsUtil.modifiedSettings(Baritone.settings()) : Baritone.settings().allSettings).stream() .filter(s -> !s.isJavaOnly()) .filter(s -> s.getName().toLowerCase(Locale.US).contains(search.toLowerCase(Locale.US))) .sorted((s1, s2) -> String.CASE_INSENSITIVE_ORDER.compare(s1.getName(), s2.getName())) .collect(Collectors.toList()); Paginator.paginate( args, new Paginator<>(toPaginate), () -> logDirect( !search.isEmpty() ? String.format("All %ssettings containing the string '%s':", viewModified ? "modified " : "", search) : String.format("All %ssettings:", viewModified ? "modified " : "") ), setting -> { MutableComponent typeComponent = Component.literal(String.format( " (%s)", settingTypeToString(setting) )); typeComponent.setStyle(typeComponent.getStyle().withColor(ChatFormatting.DARK_GRAY)); MutableComponent hoverComponent = Component.literal(""); hoverComponent.setStyle(hoverComponent.getStyle().withColor(ChatFormatting.GRAY)); hoverComponent.append(setting.getName()); hoverComponent.append(String.format("\nType: %s", settingTypeToString(setting))); hoverComponent.append(String.format("\n\nValue:\n%s", settingValueToString(setting))); hoverComponent.append(String.format("\n\nDefault Value:\n%s", settingDefaultToString(setting))); String commandSuggestion = Baritone.settings().prefix.value + String.format("set %s ", setting.getName()); MutableComponent component = Component.literal(setting.getName()); component.setStyle(component.getStyle().withColor(ChatFormatting.GRAY)); component.append(typeComponent); component.setStyle(component.getStyle() .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponent)) .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, commandSuggestion))); return component; }, FORCE_COMMAND_PREFIX + "set " + arg + " " + search ); return; } args.requireMax(1); boolean resetting = arg.equalsIgnoreCase("reset"); boolean toggling = arg.equalsIgnoreCase("toggle"); boolean doingSomething = resetting || toggling; if (resetting) { if (!args.hasAny()) { logDirect("Please specify 'all' as an argument to reset to confirm you'd really like to do this"); logDirect("ALL settings will be reset. Use the 'set modified' or 'modified' commands to see what will be reset."); logDirect("Specify a setting name instead of 'all' to only reset one setting"); } else if (args.peekString().equalsIgnoreCase("all")) { SettingsUtil.modifiedSettings(Baritone.settings()).forEach(Settings.Setting::reset); logDirect("All settings have been reset to their default values"); SettingsUtil.save(Baritone.settings()); return; } } if (toggling) { args.requireMin(1); } String settingName = doingSomething ? args.getString() : arg; Settings.Setting<?> setting = Baritone.settings().allSettings.stream() .filter(s -> s.getName().equalsIgnoreCase(settingName)) .findFirst() .orElse(null); if (setting == null) { throw new CommandInvalidTypeException(args.consumed(), "a valid setting"); } if (setting.isJavaOnly()) { // ideally it would act as if the setting didn't exist // but users will see it in Settings.java or its javadoc // so at some point we have to tell them or they will see it as a bug throw new CommandInvalidStateException(String.format("Setting %s can only be used via the api.", setting.getName())); } if (!doingSomething && !args.hasAny()) { logDirect(String.format("Value of setting %s:", setting.getName())); logDirect(settingValueToString(setting)); } else { String oldValue = settingValueToString(setting); if (resetting) { setting.reset(); } else if (toggling) { if (setting.getValueClass() != Boolean.class) { throw new CommandInvalidTypeException(args.consumed(), "a toggleable setting", "some other setting"); } //noinspection unchecked Settings.Setting<Boolean> asBoolSetting = (Settings.Setting<Boolean>) setting; asBoolSetting.value ^= true; logDirect(String.format( "Toggled setting %s to %s", setting.getName(), Boolean.toString((Boolean) setting.value) )); } else { String newValue = args.getString(); try { SettingsUtil.parseAndApply(Baritone.settings(), arg, newValue); } catch (Throwable t) { t.printStackTrace(); throw new CommandInvalidTypeException(args.consumed(), "a valid value", t); } } if (!toggling) { logDirect(String.format( "Successfully %s %s to %s", resetting ? "reset" : "set", setting.getName(), settingValueToString(setting) )); } MutableComponent oldValueComponent = Component.literal(String.format("Old value: %s", oldValue)); oldValueComponent.setStyle(oldValueComponent.getStyle() .withColor(ChatFormatting.GRAY) .withHoverEvent(new HoverEvent( HoverEvent.Action.SHOW_TEXT, Component.literal("Click to set the setting back to this value") )) .withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + String.format("set %s %s", setting.getName(), oldValue) ))); logDirect(oldValueComponent); if ((setting.getName().equals("chatControl") && !(Boolean) setting.value && !Baritone.settings().chatControlAnyway.value) || setting.getName().equals("chatControlAnyway") && !(Boolean) setting.value && !Baritone.settings().chatControl.value) { logDirect("Warning: Chat commands will no longer work. If you want to revert this change, use prefix control (if enabled) or click the old value listed above.", ChatFormatting.RED); } else if (setting.getName().equals("prefixControl") && !(Boolean) setting.value) { logDirect("Warning: Prefixed commands will no longer work. If you want to revert this change, use chat control (if enabled) or click the old value listed above.", ChatFormatting.RED); } } SettingsUtil.save(Baritone.settings()); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasAny()) { String arg = args.getString(); if (args.hasExactlyOne() && !Arrays.asList("s", "save").contains(args.peekString().toLowerCase(Locale.US))) { if (arg.equalsIgnoreCase("reset")) { return new TabCompleteHelper() .addModifiedSettings() .prepend("all") .filterPrefix(args.getString()) .stream(); } else if (arg.equalsIgnoreCase("toggle")) { return new TabCompleteHelper() .addToggleableSettings() .filterPrefix(args.getString()) .stream(); } else if (Arrays.asList("ld", "load").contains(arg.toLowerCase(Locale.US))) { // settings always use the directory of the main Minecraft instance return RelativeFile.tabComplete(args, Minecraft.getInstance().gameDirectory.toPath().resolve("baritone").toFile()); } Settings.Setting setting = Baritone.settings().byLowerName.get(arg.toLowerCase(Locale.US)); if (setting != null) { if (setting.getType() == Boolean.class) { TabCompleteHelper helper = new TabCompleteHelper(); if ((Boolean) setting.value) { helper.append("true", "false"); } else { helper.append("false", "true"); } return helper.filterPrefix(args.getString()).stream(); } else { return Stream.of(settingValueToString(setting)); } } } else if (!args.hasAny()) { return new TabCompleteHelper() .addSettings() .sortAlphabetically() .prepend("list", "modified", "reset", "toggle", "save", "load") .filterPrefix(arg) .stream(); } } return Stream.empty(); } @Override public String getShortDesc() { return "View or change settings"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Using the set command, you can manage all of Baritone's settings. Almost every aspect is controlled by these settings - go wild!", "", "Usage:", "> set - Same as `set list`", "> set list [page] - View all settings", "> set modified [page] - View modified settings", "> set <setting> - View the current value of a setting", "> set <setting> <value> - Set the value of a setting", "> set reset all - Reset ALL SETTINGS to their defaults", "> set reset <setting> - Reset a setting to its default", "> set toggle <setting> - Toggle a boolean setting", "> set save - Save all settings (this is automatic tho)", "> set load - Load settings from settings.txt", "> set load [filename] - Load settings from another file in your minecraft/baritone" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/SurfaceCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import net.minecraft.world.level.block.AirBlock; public class SurfaceCommand extends Command { protected SurfaceCommand(IBaritone baritone) { super(baritone, "surface", "top"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { final BetterBlockPos playerPos = ctx.playerFeet(); final int surfaceLevel = ctx.world().getSeaLevel(); final int worldHeight = ctx.world().getHeight(); // Ensure this command will not run if you are above the surface level and the block above you is air // As this would imply that your are already on the open surface if (playerPos.getY() > surfaceLevel && ctx.world().getBlockState(playerPos.above()).getBlock() instanceof AirBlock) { logDirect("Already at surface"); return; } final int startingYPos = Math.max(playerPos.getY(), surfaceLevel); for (int currentIteratedY = startingYPos; currentIteratedY < worldHeight; currentIteratedY++) { final BetterBlockPos newPos = new BetterBlockPos(playerPos.getX(), currentIteratedY, playerPos.getZ()); if (!(ctx.world().getBlockState(newPos).getBlock() instanceof AirBlock) && newPos.getY() > playerPos.getY()) { Goal goal = new GoalBlock(newPos.above()); logDirect(String.format("Going to: %s", goal.toString())); baritone.getCustomGoalProcess().setGoalAndPath(goal); return; } } logDirect("No higher location found"); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Used to get out of caves, mines, ..."; } @Override public List<String> getLongDesc() { return Arrays.asList( "The surface/top command tells Baritone to head towards the closest surface-like area.", "", "This can be the surface or the highest available air space, depending on circumstances.", "", "Usage:", "> surface - Used to get out of caves, mines, ...", "> top - Used to get out of caves, mines, ..." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/ThisWayCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalXZ; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class ThisWayCommand extends Command { public ThisWayCommand(IBaritone baritone) { super(baritone, "thisway", "forward"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireExactly(1); GoalXZ goal = GoalXZ.fromDirection( ctx.playerFeetAsVec(), ctx.player().getYHeadRot(), args.getAs(Double.class) ); baritone.getCustomGoalProcess().setGoal(goal); logDirect(String.format("Goal: %s", goal)); } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Travel in your current direction"; } @Override public List<String> getLongDesc() { return Arrays.asList( "Creates a GoalXZ some amount of blocks in the direction you're currently looking", "", "Usage:", "> thisway <distance> - makes a GoalXZ distance blocks in front of you" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/TunnelCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalStrictDirection; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; public class TunnelCommand extends Command { public TunnelCommand(IBaritone baritone) { super(baritone, "tunnel"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(3); if (args.hasExactly(3)) { boolean cont = true; int height = Integer.parseInt(args.getArgs().get(0).getValue()); int width = Integer.parseInt(args.getArgs().get(1).getValue()); int depth = Integer.parseInt(args.getArgs().get(2).getValue()); if (width < 1 || height < 2 || depth < 1 || height > ctx.world().getMaxBuildHeight()){ logDirect("Width and depth must at least be 1 block; Height must at least be 2 blocks, and cannot be greater than the build limit."); cont = false; } if (cont) { height--; width--; BlockPos corner1; BlockPos corner2; Direction enumFacing = ctx.player().getDirection(); int addition = ((width % 2 == 0) ? 0 : 1); switch (enumFacing) { case EAST: corner1 = new BlockPos(ctx.playerFeet().x, ctx.playerFeet().y, ctx.playerFeet().z - width / 2); corner2 = new BlockPos(ctx.playerFeet().x + depth, ctx.playerFeet().y + height, ctx.playerFeet().z + width / 2 + addition); break; case WEST: corner1 = new BlockPos(ctx.playerFeet().x, ctx.playerFeet().y, ctx.playerFeet().z + width / 2 + addition); corner2 = new BlockPos(ctx.playerFeet().x - depth, ctx.playerFeet().y + height, ctx.playerFeet().z - width / 2); break; case NORTH: corner1 = new BlockPos(ctx.playerFeet().x - width / 2, ctx.playerFeet().y, ctx.playerFeet().z); corner2 = new BlockPos(ctx.playerFeet().x + width / 2 + addition, ctx.playerFeet().y + height, ctx.playerFeet().z - depth); break; case SOUTH: corner1 = new BlockPos(ctx.playerFeet().x + width / 2 + addition, ctx.playerFeet().y, ctx.playerFeet().z); corner2 = new BlockPos(ctx.playerFeet().x - width / 2, ctx.playerFeet().y + height, ctx.playerFeet().z + depth); break; default: throw new IllegalStateException("Unexpected value: " + enumFacing); } logDirect(String.format("Creating a tunnel %s block(s) high, %s block(s) wide, and %s block(s) deep", height + 1, width + 1, depth)); baritone.getBuilderProcess().clearArea(corner1, corner2); } } else { Goal goal = new GoalStrictDirection( ctx.playerFeet(), ctx.player().getDirection() ); baritone.getCustomGoalProcess().setGoalAndPath(goal); logDirect(String.format("Goal: %s", goal.toString())); } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "Set a goal to tunnel in your current direction"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The tunnel command sets a goal that tells Baritone to mine completely straight in the direction that you're facing.", "", "Usage:", "> tunnel - No arguments, mines in a 1x2 radius.", "> tunnel <height> <width> <depth> - Tunnels in a user defined height, width and depth." ); } } ================================================ FILE: src/main/java/baritone/command/defaults/VersionCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class VersionCommand extends Command { public VersionCommand(IBaritone baritone) { super(baritone, "version"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); String version = getClass().getPackage().getImplementationVersion(); if (version == null) { throw new CommandInvalidStateException("Null version (this is normal in a dev environment)"); } else { logDirect(String.format("You are running Baritone v%s", version)); } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) { return Stream.empty(); } @Override public String getShortDesc() { return "View the Baritone version"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The version command prints the version of Baritone you're currently running.", "", "Usage:", "> version - View version information, if present" ); } } ================================================ FILE: src/main/java/baritone/command/defaults/WaypointsCommand.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.defaults; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.cache.IWaypoint; import baritone.api.cache.IWorldData; import baritone.api.cache.Waypoint; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.ForWaypoints; import baritone.api.command.datatypes.RelativeBlockPos; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.helpers.Paginator; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class WaypointsCommand extends Command { private Map<IWorldData, List<IWaypoint>> deletedWaypoints = new HashMap<>(); public WaypointsCommand(IBaritone baritone) { super(baritone, "waypoints", "waypoint", "wp"); } @Override public void execute(String label, IArgConsumer args) throws CommandException { Action action = args.hasAny() ? Action.getByName(args.getString()) : Action.LIST; if (action == null) { throw new CommandInvalidTypeException(args.consumed(), "an action"); } BiFunction<IWaypoint, Action, Component> toComponent = (waypoint, _action) -> { MutableComponent component = Component.literal(""); MutableComponent tagComponent = Component.literal(waypoint.getTag().name() + " "); tagComponent.setStyle(tagComponent.getStyle().withColor(ChatFormatting.GRAY)); String name = waypoint.getName(); MutableComponent nameComponent = Component.literal(!name.isEmpty() ? name : "<empty>"); nameComponent.setStyle(nameComponent.getStyle().withColor(!name.isEmpty() ? ChatFormatting.GRAY : ChatFormatting.DARK_GRAY)); MutableComponent timestamp = Component.literal(" @ " + new Date(waypoint.getCreationTimestamp())); timestamp.setStyle(timestamp.getStyle().withColor(ChatFormatting.DARK_GRAY)); component.append(tagComponent); component.append(nameComponent); component.append(timestamp); component.setStyle(component.getStyle() .withHoverEvent(new HoverEvent( HoverEvent.Action.SHOW_TEXT, Component.literal("Click to select") )) .withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format( "%s%s %s %s @ %d", FORCE_COMMAND_PREFIX, label, _action.names[0], waypoint.getTag().getName(), waypoint.getCreationTimestamp() )) )); return component; }; Function<IWaypoint, Component> transform = waypoint -> toComponent.apply(waypoint, action == Action.LIST ? Action.INFO : action); if (action == Action.LIST) { IWaypoint.Tag tag = args.hasAny() ? IWaypoint.Tag.getByName(args.peekString()) : null; if (tag != null) { args.get(); } IWaypoint[] waypoints = tag != null ? ForWaypoints.getWaypointsByTag(this.baritone, tag) : ForWaypoints.getWaypoints(this.baritone); if (waypoints.length > 0) { args.requireMax(1); Paginator.paginate( args, waypoints, () -> logDirect( tag != null ? String.format("All waypoints by tag %s:", tag.name()) : "All waypoints:" ), transform, String.format( "%s%s %s%s", FORCE_COMMAND_PREFIX, label, action.names[0], tag != null ? " " + tag.getName() : "" ) ); } else { args.requireMax(0); throw new CommandInvalidStateException( tag != null ? "No waypoints found by that tag" : "No waypoints found" ); } } else if (action == Action.SAVE) { IWaypoint.Tag tag = args.hasAny() ? IWaypoint.Tag.getByName(args.peekString()) : null; if (tag == null) { tag = IWaypoint.Tag.USER; } else { args.get(); } String name = (args.hasExactlyOne() || args.hasExactly(4)) ? args.getString() : ""; BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, ctx.playerFeet()) : ctx.playerFeet(); args.requireMax(0); IWaypoint waypoint = new Waypoint(name, tag, pos); ForWaypoints.waypoints(this.baritone).addWaypoint(waypoint); MutableComponent component = Component.literal("Waypoint added: "); component.setStyle(component.getStyle().withColor(ChatFormatting.GRAY)); component.append(toComponent.apply(waypoint, Action.INFO)); logDirect(component); } else if (action == Action.CLEAR) { args.requireMax(1); String name = args.getString(); IWaypoint.Tag tag = IWaypoint.Tag.getByName(name); if (tag == null) { throw new CommandInvalidStateException("Invalid tag, \"" + name + "\""); } IWaypoint[] waypoints = ForWaypoints.getWaypointsByTag(this.baritone, tag); for (IWaypoint waypoint : waypoints) { ForWaypoints.waypoints(this.baritone).removeWaypoint(waypoint); } deletedWaypoints.computeIfAbsent(baritone.getWorldProvider().getCurrentWorld(), k -> new ArrayList<>()).addAll(Arrays.<IWaypoint>asList(waypoints)); MutableComponent textComponent = Component.literal(String.format("Cleared %d waypoints, click to restore them", waypoints.length)); textComponent.setStyle(textComponent.getStyle().withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format( "%s%s restore @ %s", FORCE_COMMAND_PREFIX, label, Stream.of(waypoints).map(wp -> Long.toString(wp.getCreationTimestamp())).collect(Collectors.joining(" ")) ) ))); logDirect(textComponent); } else if (action == Action.RESTORE) { List<IWaypoint> waypoints = new ArrayList<>(); List<IWaypoint> deletedWaypoints = this.deletedWaypoints.getOrDefault(baritone.getWorldProvider().getCurrentWorld(), Collections.emptyList()); if (args.peekString().equals("@")) { args.get(); // no args.requireMin(1) because if the user clears an empty tag there is nothing to restore while (args.hasAny()) { long timestamp = args.getAs(Long.class); for (IWaypoint waypoint : deletedWaypoints) { if (waypoint.getCreationTimestamp() == timestamp) { waypoints.add(waypoint); break; } } } } else { args.requireExactly(1); int size = deletedWaypoints.size(); int amount = Math.min(size, args.getAs(Integer.class)); waypoints = new ArrayList<>(deletedWaypoints.subList(size - amount, size)); } waypoints.forEach(ForWaypoints.waypoints(this.baritone)::addWaypoint); deletedWaypoints.removeIf(waypoints::contains); logDirect(String.format("Restored %d waypoints", waypoints.size())); } else { IWaypoint[] waypoints = args.getDatatypeFor(ForWaypoints.INSTANCE); IWaypoint waypoint = null; if (args.hasAny() && args.peekString().equals("@")) { args.requireExactly(2); args.get(); long timestamp = args.getAs(Long.class); for (IWaypoint iWaypoint : waypoints) { if (iWaypoint.getCreationTimestamp() == timestamp) { waypoint = iWaypoint; break; } } if (waypoint == null) { throw new CommandInvalidStateException("Timestamp was specified but no waypoint was found"); } } else { switch (waypoints.length) { case 0: throw new CommandInvalidStateException("No waypoints found"); case 1: waypoint = waypoints[0]; break; default: break; } } if (waypoint == null) { args.requireMax(1); Paginator.paginate( args, waypoints, () -> logDirect("Multiple waypoints were found:"), transform, String.format( "%s%s %s %s", FORCE_COMMAND_PREFIX, label, action.names[0], args.consumedString() ) ); } else { if (action == Action.INFO) { logDirect(transform.apply(waypoint)); logDirect(String.format("Position: %s", waypoint.getLocation())); MutableComponent deleteComponent = Component.literal("Click to delete this waypoint"); deleteComponent.setStyle(deleteComponent.getStyle().withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format( "%s%s delete %s @ %d", FORCE_COMMAND_PREFIX, label, waypoint.getTag().getName(), waypoint.getCreationTimestamp() ) ))); MutableComponent goalComponent = Component.literal("Click to set goal to this waypoint"); goalComponent.setStyle(goalComponent.getStyle().withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format( "%s%s goal %s @ %d", FORCE_COMMAND_PREFIX, label, waypoint.getTag().getName(), waypoint.getCreationTimestamp() ) ))); MutableComponent recreateComponent = Component.literal("Click to show a command to recreate this waypoint"); recreateComponent.setStyle(recreateComponent.getStyle().withClickEvent(new ClickEvent( ClickEvent.Action.SUGGEST_COMMAND, String.format( "%s%s save %s %s %s %s %s", Baritone.settings().prefix.value, // This uses the normal prefix because it is run by the user. label, waypoint.getTag().getName(), waypoint.getName(), waypoint.getLocation().x, waypoint.getLocation().y, waypoint.getLocation().z ) ))); MutableComponent backComponent = Component.literal("Click to return to the waypoints list"); backComponent.setStyle(backComponent.getStyle().withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format( "%s%s list", FORCE_COMMAND_PREFIX, label ) ))); logDirect(deleteComponent); logDirect(goalComponent); logDirect(recreateComponent); logDirect(backComponent); } else if (action == Action.DELETE) { ForWaypoints.waypoints(this.baritone).removeWaypoint(waypoint); deletedWaypoints.computeIfAbsent(baritone.getWorldProvider().getCurrentWorld(), k -> new ArrayList<>()).add(waypoint); MutableComponent textComponent = Component.literal("That waypoint has successfully been deleted, click to restore it"); textComponent.setStyle(textComponent.getStyle().withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, String.format( "%s%s restore @ %s", FORCE_COMMAND_PREFIX, label, waypoint.getCreationTimestamp() ) ))); logDirect(textComponent); } else if (action == Action.GOAL) { Goal goal = new GoalBlock(waypoint.getLocation()); baritone.getCustomGoalProcess().setGoal(goal); logDirect(String.format("Goal: %s", goal)); } else if (action == Action.GOTO) { Goal goal = new GoalBlock(waypoint.getLocation()); baritone.getCustomGoalProcess().setGoalAndPath(goal); logDirect(String.format("Going to: %s", goal)); } } } } @Override public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasAny()) { if (args.hasExactlyOne()) { return new TabCompleteHelper() .append(Action.getAllNames()) .sortAlphabetically() .filterPrefix(args.getString()) .stream(); } else { Action action = Action.getByName(args.getString()); if (args.hasExactlyOne()) { if (action == Action.LIST || action == Action.SAVE || action == Action.CLEAR) { return new TabCompleteHelper() .append(IWaypoint.Tag.getAllNames()) .sortAlphabetically() .filterPrefix(args.getString()) .stream(); } else if (action == Action.RESTORE) { return Stream.empty(); } else { return args.tabCompleteDatatype(ForWaypoints.INSTANCE); } } else if (args.has(3) && action == Action.SAVE) { args.get(); args.get(); return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); } } } return Stream.empty(); } @Override public String getShortDesc() { return "Manage waypoints"; } @Override public List<String> getLongDesc() { return Arrays.asList( "The waypoint command allows you to manage Baritone's waypoints.", "", "Waypoints can be used to mark positions for later. Waypoints are each given a tag and an optional name.", "", "Note that the info, delete, and goal commands let you specify a waypoint by tag. If there is more than one waypoint with a certain tag, then they will let you select which waypoint you mean.", "", "Missing arguments for the save command use the USER tag, creating an unnamed waypoint and your current position as defaults.", "", "Usage:", "> wp [l/list] - List all waypoints.", "> wp <l/list> <tag> - List all waypoints by tag.", "> wp <s/save> - Save an unnamed USER waypoint at your current position", "> wp <s/save> [tag] [name] [pos] - Save a waypoint with the specified tag, name and position.", "> wp <i/info/show> <tag/name> - Show info on a waypoint by tag or name.", "> wp <d/delete> <tag/name> - Delete a waypoint by tag or name.", "> wp <restore> <n> - Restore the last n deleted waypoints.", "> wp <c/clear> <tag> - Delete all waypoints with the specified tag.", "> wp <g/goal> <tag/name> - Set a goal to a waypoint by tag or name.", "> wp <goto> <tag/name> - Set a goal to a waypoint by tag or name and start pathing." ); } private enum Action { LIST("list", "get", "l"), CLEAR("clear", "c"), SAVE("save", "s"), INFO("info", "show", "i"), DELETE("delete", "d"), RESTORE("restore"), GOAL("goal", "g"), GOTO("goto"); private final String[] names; Action(String... names) { this.names = names; } public static Action getByName(String name) { for (Action action : Action.values()) { for (String alias : action.names) { if (alias.equalsIgnoreCase(name)) { return action; } } } return null; } public static String[] getAllNames() { Set<String> names = new HashSet<>(); for (Action action : Action.values()) { names.addAll(Arrays.asList(action.names)); } return names.toArray(new String[0]); } } } ================================================ FILE: src/main/java/baritone/command/manager/CommandManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.command.manager; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandUnhandledException; import baritone.api.command.exception.ICommandException; import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.command.manager.ICommandManager; import baritone.api.command.registry.Registry; import baritone.command.argument.ArgConsumer; import baritone.command.argument.CommandArguments; import baritone.command.defaults.DefaultCommands; import net.minecraft.util.Tuple; import java.util.List; import java.util.Locale; import java.util.stream.Stream; /** * The default, internal implementation of {@link ICommandManager} * * @author Brady * @since 9/21/2019 */ public class CommandManager implements ICommandManager { private final Registry<ICommand> registry = new Registry<>(); private final Baritone baritone; public CommandManager(Baritone baritone) { this.baritone = baritone; DefaultCommands.createAll(baritone).forEach(this.registry::register); } @Override public IBaritone getBaritone() { return this.baritone; } @Override public Registry<ICommand> getRegistry() { return this.registry; } @Override public ICommand getCommand(String name) { for (ICommand command : this.registry.entries) { if (command.getNames().contains(name.toLowerCase(Locale.US))) { return command; } } return null; } @Override public boolean execute(String string) { return this.execute(expand(string)); } @Override public boolean execute(Tuple<String, List<ICommandArgument>> expanded) { ExecutionWrapper execution = this.from(expanded); if (execution != null) { execution.execute(); } return execution != null; } @Override public Stream<String> tabComplete(Tuple<String, List<ICommandArgument>> expanded) { ExecutionWrapper execution = this.from(expanded); return execution == null ? Stream.empty() : execution.tabComplete(); } @Override public Stream<String> tabComplete(String prefix) { Tuple<String, List<ICommandArgument>> pair = expand(prefix, true); String label = pair.getA(); List<ICommandArgument> args = pair.getB(); if (args.isEmpty()) { return new TabCompleteHelper() .addCommands(this.baritone.getCommandManager()) .filterPrefix(label) .stream(); } else { return tabComplete(pair); } } private ExecutionWrapper from(Tuple<String, List<ICommandArgument>> expanded) { String label = expanded.getA(); ArgConsumer args = new ArgConsumer(this, expanded.getB()); ICommand command = this.getCommand(label); return command == null ? null : new ExecutionWrapper(command, label, args); } private static Tuple<String, List<ICommandArgument>> expand(String string, boolean preserveEmptyLast) { String label = string.split("\\s", 2)[0]; List<ICommandArgument> args = CommandArguments.from(string.substring(label.length()), preserveEmptyLast); return new Tuple<>(label, args); } public static Tuple<String, List<ICommandArgument>> expand(String string) { return expand(string, false); } private static final class ExecutionWrapper { private ICommand command; private String label; private ArgConsumer args; private ExecutionWrapper(ICommand command, String label, ArgConsumer args) { this.command = command; this.label = label; this.args = args; } private void execute() { try { this.command.execute(this.label, this.args); } catch (Throwable t) { // Create a handleable exception, wrap if needed ICommandException exception = t instanceof ICommandException ? (ICommandException) t : new CommandUnhandledException(t); exception.handle(command, args.getArgs()); } } private Stream<String> tabComplete() { try { return this.command.tabComplete(this.label, this.args); } catch (CommandException ignored) { // NOP } catch (Throwable t) { t.printStackTrace(); } return Stream.empty(); } } } ================================================ FILE: src/main/java/baritone/event/GameEventHandler.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.event; import baritone.Baritone; import baritone.api.event.events.*; import baritone.api.event.events.type.EventState; import baritone.api.event.listener.IEventBus; import baritone.api.event.listener.IGameEventListener; import baritone.api.utils.Helper; import baritone.api.utils.Pair; import baritone.cache.CachedChunk; import baritone.cache.WorldProvider; import baritone.utils.BlockStateInterface; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * @author Brady * @since 7/31/2018 */ public final class GameEventHandler implements IEventBus, Helper { private final Baritone baritone; private final List<IGameEventListener> listeners = new CopyOnWriteArrayList<>(); public GameEventHandler(Baritone baritone) { this.baritone = baritone; } @Override public final void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.IN) { try { baritone.bsi = new BlockStateInterface(baritone.getPlayerContext(), true); } catch (Exception ex) { ex.printStackTrace(); baritone.bsi = null; } } else { baritone.bsi = null; } listeners.forEach(l -> l.onTick(event)); } @Override public void onPostTick(TickEvent event) { listeners.forEach(l -> l.onPostTick(event)); } @Override public final void onPlayerUpdate(PlayerUpdateEvent event) { listeners.forEach(l -> l.onPlayerUpdate(event)); } @Override public final void onSendChatMessage(ChatEvent event) { listeners.forEach(l -> l.onSendChatMessage(event)); } @Override public void onPreTabComplete(TabCompleteEvent event) { listeners.forEach(l -> l.onPreTabComplete(event)); } @Override public void onChunkEvent(ChunkEvent event) { EventState state = event.getState(); ChunkEvent.Type type = event.getType(); Level world = baritone.getPlayerContext().world(); // Whenever the server sends us to another dimension, chunks are unloaded // technically after the new world has been loaded, so we perform a check // to make sure the chunk being unloaded is already loaded. boolean isPreUnload = state == EventState.PRE && type == ChunkEvent.Type.UNLOAD && world.getChunkSource().getChunk(event.getX(), event.getZ(), null, false) != null; if (event.isPostPopulate() || isPreUnload) { baritone.getWorldProvider().ifWorldLoaded(worldData -> { LevelChunk chunk = world.getChunk(event.getX(), event.getZ()); worldData.getCachedWorld().queueForPacking(chunk); }); } listeners.forEach(l -> l.onChunkEvent(event)); } @Override public void onBlockChange(BlockChangeEvent event) { if (Baritone.settings().repackOnAnyBlockChange.value) { final boolean keepingTrackOf = event.getBlocks().stream() .map(Pair::second).map(BlockState::getBlock) .anyMatch(CachedChunk.BLOCKS_TO_KEEP_TRACK_OF::contains); if (keepingTrackOf) { baritone.getWorldProvider().ifWorldLoaded(worldData -> { final Level world = baritone.getPlayerContext().world(); ChunkPos pos = event.getChunkPos(); worldData.getCachedWorld().queueForPacking(world.getChunk(pos.x, pos.z)); }); } } listeners.forEach(l -> l.onBlockChange(event)); } @Override public final void onRenderPass(RenderEvent event) { listeners.forEach(l -> l.onRenderPass(event)); } @Override public final void onWorldEvent(WorldEvent event) { WorldProvider cache = baritone.getWorldProvider(); if (event.getState() == EventState.POST) { cache.closeWorld(); if (event.getWorld() != null) { cache.initWorld(event.getWorld()); } } listeners.forEach(l -> l.onWorldEvent(event)); } @Override public final void onSendPacket(PacketEvent event) { listeners.forEach(l -> l.onSendPacket(event)); } @Override public final void onReceivePacket(PacketEvent event) { listeners.forEach(l -> l.onReceivePacket(event)); } @Override public void onPlayerRotationMove(RotationMoveEvent event) { listeners.forEach(l -> l.onPlayerRotationMove(event)); } @Override public void onPlayerSprintState(SprintStateEvent event) { listeners.forEach(l -> l.onPlayerSprintState(event)); } @Override public void onBlockInteract(BlockInteractEvent event) { listeners.forEach(l -> l.onBlockInteract(event)); } @Override public void onPlayerDeath() { listeners.forEach(IGameEventListener::onPlayerDeath); } @Override public void onPathEvent(PathEvent event) { listeners.forEach(l -> l.onPathEvent(event)); } @Override public final void registerEventListener(IGameEventListener listener) { this.listeners.add(listener); } } ================================================ FILE: src/main/java/baritone/pathing/calc/AStarPathFinder.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc; import baritone.Baritone; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.ActionCosts; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.pathing.calc.openset.BinaryHeapOpenSet; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Moves; import baritone.utils.pathing.BetterWorldBorder; import baritone.utils.pathing.Favoring; import baritone.utils.pathing.MutableMoveResult; import java.util.Optional; /** * The actual A* pathfinding * * @author leijurv */ public final class AStarPathFinder extends AbstractNodeCostSearch { private final Favoring favoring; private final CalculationContext calcContext; public AStarPathFinder(BetterBlockPos realStart, int startX, int startY, int startZ, Goal goal, Favoring favoring, CalculationContext context) { super(realStart, startX, startY, startZ, goal, context); this.favoring = favoring; this.calcContext = context; } @Override protected Optional<IPath> calculate0(long primaryTimeout, long failureTimeout) { int minY = calcContext.world.dimensionType().minY(); int height = calcContext.world.dimensionType().height(); startNode = getNodeAtPosition(startX, startY, startZ, BetterBlockPos.longHash(startX, startY, startZ)); startNode.cost = 0; startNode.combinedCost = startNode.estimatedCostToGoal; BinaryHeapOpenSet openSet = new BinaryHeapOpenSet(); openSet.insert(startNode); double[] bestHeuristicSoFar = new double[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i]) for (int i = 0; i < bestHeuristicSoFar.length; i++) { bestHeuristicSoFar[i] = startNode.estimatedCostToGoal; bestSoFar[i] = startNode; } MutableMoveResult res = new MutableMoveResult(); BetterWorldBorder worldBorder = new BetterWorldBorder(calcContext.world.getWorldBorder()); long startTime = System.currentTimeMillis(); boolean slowPath = Baritone.settings().slowPath.value; if (slowPath) { logDebug("slowPath is on, path timeout will be " + Baritone.settings().slowPathTimeoutMS.value + "ms instead of " + primaryTimeout + "ms"); } long primaryTimeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS.value : primaryTimeout); long failureTimeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS.value : failureTimeout); boolean failing = true; int numNodes = 0; int numMovementsConsidered = 0; int numEmptyChunk = 0; boolean isFavoring = !favoring.isEmpty(); int timeCheckInterval = 1 << 6; int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.value; // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior double minimumImprovement = Baritone.settings().minimumImprovementRepropagation.value ? MIN_IMPROVEMENT : 0; Moves[] allMoves = Moves.values(); while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && !cancelRequested) { if ((numNodes & (timeCheckInterval - 1)) == 0) { // only call this once every 64 nodes (about half a millisecond) long now = System.currentTimeMillis(); // since nanoTime is slow on windows (takes many microseconds) if (now - failureTimeoutTime >= 0 || (!failing && now - primaryTimeoutTime >= 0)) { break; } } if (slowPath) { try { Thread.sleep(Baritone.settings().slowPathTimeDelayMS.value); } catch (InterruptedException ignored) {} } PathNode currentNode = openSet.removeLowest(); mostRecentConsidered = currentNode; numNodes++; if (goal.isInGoal(currentNode.x, currentNode.y, currentNode.z)) { logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, " + numMovementsConsidered + " movements considered"); return Optional.of(new Path(realStart, startNode, currentNode, numNodes, goal, calcContext)); } for (Moves moves : allMoves) { int newX = currentNode.x + moves.xOffset; int newZ = currentNode.z + moves.zOffset; if ((newX >> 4 != currentNode.x >> 4 || newZ >> 4 != currentNode.z >> 4) && !calcContext.isLoaded(newX, newZ)) { // only need to check if the destination is a loaded chunk if it's in a different chunk than the start of the movement if (!moves.dynamicXZ) { // only increment the counter if the movement would have gone out of bounds guaranteed numEmptyChunk++; } continue; } if (!moves.dynamicXZ && !worldBorder.entirelyContains(newX, newZ)) { continue; } if (currentNode.y + moves.yOffset > height || currentNode.y + moves.yOffset < minY) { continue; } res.reset(); moves.apply(calcContext, currentNode.x, currentNode.y, currentNode.z, res); numMovementsConsidered++; double actionCost = res.cost; if (actionCost >= ActionCosts.COST_INF) { continue; } if (actionCost <= 0 || Double.isNaN(actionCost)) { throw new IllegalStateException(String.format( "%s from %s %s %s calculated implausible cost %s", moves, SettingsUtil.maybeCensor(currentNode.x), SettingsUtil.maybeCensor(currentNode.y), SettingsUtil.maybeCensor(currentNode.z), actionCost)); } // check destination after verifying it's not COST_INF -- some movements return COST_INF without adjusting the destination if (moves.dynamicXZ && !worldBorder.entirelyContains(res.x, res.z)) { // see issue #218 continue; } if (!moves.dynamicXZ && (res.x != newX || res.z != newZ)) { throw new IllegalStateException(String.format( "%s from %s %s %s ended at x z %s %s instead of %s %s", moves, SettingsUtil.maybeCensor(currentNode.x), SettingsUtil.maybeCensor(currentNode.y), SettingsUtil.maybeCensor(currentNode.z), SettingsUtil.maybeCensor(res.x), SettingsUtil.maybeCensor(res.z), SettingsUtil.maybeCensor(newX), SettingsUtil.maybeCensor(newZ))); } if (!moves.dynamicY && res.y != currentNode.y + moves.yOffset) { throw new IllegalStateException(String.format( "%s from %s %s %s ended at y %s instead of %s", moves, SettingsUtil.maybeCensor(currentNode.x), SettingsUtil.maybeCensor(currentNode.y), SettingsUtil.maybeCensor(currentNode.z), SettingsUtil.maybeCensor(res.y), SettingsUtil.maybeCensor(currentNode.y + moves.yOffset))); } long hashCode = BetterBlockPos.longHash(res.x, res.y, res.z); if (isFavoring) { // see issue #18 actionCost *= favoring.calculate(hashCode); } PathNode neighbor = getNodeAtPosition(res.x, res.y, res.z, hashCode); double tentativeCost = currentNode.cost + actionCost; if (neighbor.cost - tentativeCost > minimumImprovement) { neighbor.previous = currentNode; neighbor.cost = tentativeCost; neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal; if (neighbor.isOpen()) { openSet.update(neighbor); } else { openSet.insert(neighbor);//dont double count, dont insert into open set if it's already there } for (int i = 0; i < COEFFICIENTS.length; i++) { double heuristic = neighbor.estimatedCostToGoal + neighbor.cost / COEFFICIENTS[i]; if (bestHeuristicSoFar[i] - heuristic > minimumImprovement) { bestHeuristicSoFar[i] = heuristic; bestSoFar[i] = neighbor; if (failing && getDistFromStartSq(neighbor) > MIN_DIST_PATH * MIN_DIST_PATH) { failing = false; } } } } } } if (cancelRequested) { return Optional.empty(); } System.out.println(numMovementsConsidered + " movements considered"); System.out.println("Open set size: " + openSet.size()); System.out.println("PathNode map size: " + mapSize()); System.out.println((int) (numNodes * 1.0 / ((System.currentTimeMillis() - startTime) / 1000F)) + " nodes per second"); Optional<IPath> result = bestSoFar(true, numNodes); if (result.isPresent()) { logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, " + numMovementsConsidered + " movements considered"); } return result; } } ================================================ FILE: src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc; import baritone.Baritone; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.calc.IPathFinder; import baritone.api.pathing.goals.Goal; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; import baritone.api.utils.PathCalculationResult; import baritone.pathing.movement.CalculationContext; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.Optional; /** * Any pathfinding algorithm that keeps track of nodes recursively by their cost (e.g. A*, dijkstra) * * @author leijurv */ public abstract class AbstractNodeCostSearch implements IPathFinder, Helper { protected final BetterBlockPos realStart; protected final int startX; protected final int startY; protected final int startZ; protected final Goal goal; private final CalculationContext context; /** * @see <a href="https://github.com/cabaletta/baritone/issues/107">Issue #107</a> */ private final Long2ObjectOpenHashMap<PathNode> map; protected PathNode startNode; protected PathNode mostRecentConsidered; protected final PathNode[] bestSoFar = new PathNode[COEFFICIENTS.length]; private volatile boolean isFinished; protected boolean cancelRequested; /** * This is really complicated and hard to explain. I wrote a comment in the old version of MineBot but it was so * long it was easier as a Google Doc (because I could insert charts). * * @see <a href="https://docs.google.com/document/d/1WVHHXKXFdCR1Oz__KtK8sFqyvSwJN_H4lftkHFgmzlc/edit">here</a> */ protected static final double[] COEFFICIENTS = {1.5, 2, 2.5, 3, 4, 5, 10}; /** * If a path goes less than 5 blocks and doesn't make it to its goal, it's not worth considering. */ protected static final double MIN_DIST_PATH = 5; /** * there are floating point errors caused by random combinations of traverse and diagonal over a flat area * that means that sometimes there's a cost improvement of like 10 ^ -16 * it's not worth the time to update the costs, decrease-key the heap, potentially repropagate, etc * <p> * who cares about a hundredth of a tick? that's half a millisecond for crying out loud! */ protected static final double MIN_IMPROVEMENT = 0.01; AbstractNodeCostSearch(BetterBlockPos realStart, int startX, int startY, int startZ, Goal goal, CalculationContext context) { this.realStart = realStart; this.startX = startX; this.startY = startY; this.startZ = startZ; this.goal = goal; this.context = context; this.map = new Long2ObjectOpenHashMap<>(Baritone.settings().pathingMapDefaultSize.value, Baritone.settings().pathingMapLoadFactor.value); } public void cancel() { cancelRequested = true; } @Override public synchronized PathCalculationResult calculate(long primaryTimeout, long failureTimeout) { if (isFinished) { throw new IllegalStateException("Path finder cannot be reused!"); } cancelRequested = false; try { IPath path = calculate0(primaryTimeout, failureTimeout).map(IPath::postProcess).orElse(null); if (cancelRequested) { return new PathCalculationResult(PathCalculationResult.Type.CANCELLATION); } if (path == null) { return new PathCalculationResult(PathCalculationResult.Type.FAILURE); } int previousLength = path.length(); path = path.cutoffAtLoadedChunks(context.bsi); if (path.length() < previousLength) { Helper.HELPER.logDebug("Cutting off path at edge of loaded chunks"); Helper.HELPER.logDebug("Length decreased by " + (previousLength - path.length())); } else { Helper.HELPER.logDebug("Path ends within loaded chunks"); } previousLength = path.length(); path = path.staticCutoff(goal); if (path.length() < previousLength) { Helper.HELPER.logDebug("Static cutoff " + previousLength + " to " + path.length()); } if (goal.isInGoal(path.getDest())) { return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_TO_GOAL, path); } else { return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_SEGMENT, path); } } catch (Exception e) { Helper.HELPER.logDirect("Pathing exception: " + e); e.printStackTrace(); return new PathCalculationResult(PathCalculationResult.Type.EXCEPTION); } finally { // this is run regardless of what exception may or may not be raised by calculate0 isFinished = true; } } protected abstract Optional<IPath> calculate0(long primaryTimeout, long failureTimeout); /** * Determines the distance squared from the specified node to the start * node. Intended for use in distance comparison, rather than anything that * considers the real distance value, hence the "sq". * * @param n A node * @return The distance, squared */ protected double getDistFromStartSq(PathNode n) { int xDiff = n.x - startX; int yDiff = n.y - startY; int zDiff = n.z - startZ; return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; } /** * Attempts to search the block position hashCode long to {@link PathNode} map * for the node mapped to the specified pos. If no node is found, * a new node is created. * * @param x The x position of the node * @param y The y position of the node * @param z The z position of the node * @param hashCode The hash code of the node, provided by {@link BetterBlockPos#longHash(int, int, int)} * @return The associated node * @see <a href="https://github.com/cabaletta/baritone/issues/107">Issue #107</a> */ protected PathNode getNodeAtPosition(int x, int y, int z, long hashCode) { PathNode node = map.get(hashCode); if (node == null) { node = new PathNode(x, y, z, goal); map.put(hashCode, node); } return node; } @Override public Optional<IPath> pathToMostRecentNodeConsidered() { return Optional.ofNullable(mostRecentConsidered).map(node -> new Path(realStart, startNode, node, 0, goal, context)); } @Override public Optional<IPath> bestPathSoFar() { return bestSoFar(false, 0); } protected Optional<IPath> bestSoFar(boolean logInfo, int numNodes) { if (startNode == null) { return Optional.empty(); } double bestDist = 0; for (int i = 0; i < COEFFICIENTS.length; i++) { if (bestSoFar[i] == null) { continue; } double dist = getDistFromStartSq(bestSoFar[i]); if (dist > bestDist) { bestDist = dist; } if (dist > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared if (logInfo) { if (COEFFICIENTS[i] >= 3) { System.out.println("Warning: cost coefficient is greater than three! Probably means that"); System.out.println("the path I found is pretty terrible (like sneak-bridging for dozens of blocks)"); System.out.println("But I'm going to do it anyway, because yolo"); } System.out.println("Path goes for " + Math.sqrt(dist) + " blocks"); logDebug("A* cost coefficient " + COEFFICIENTS[i]); } return Optional.of(new Path(realStart, startNode, bestSoFar[i], numNodes, goal, context)); } } // instead of returning bestSoFar[0], be less misleading // if it actually won't find any path, don't make them think it will by rendering a dark blue that will never actually happen if (logInfo) { logDebug("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + Math.sqrt(bestDist) + " blocks"); logDebug("No path found =("); logNotification("No path found =(", true); } return Optional.empty(); } @Override public final boolean isFinished() { return isFinished; } @Override public final Goal getGoal() { return goal; } public BetterBlockPos getStart() { return new BetterBlockPos(startX, startY, startZ); } protected int mapSize() { return map.size(); } } ================================================ FILE: src/main/java/baritone/pathing/calc/Path.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.IMovement; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.Moves; import baritone.pathing.path.CutoffPath; import baritone.utils.pathing.PathBase; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * A node based implementation of IPath * * @author leijurv */ class Path extends PathBase { /** * The start position of this path */ private final BetterBlockPos start; /** * The end position of this path */ private final BetterBlockPos end; /** * The blocks on the path. Guaranteed that path.get(0) equals start and * path.get(path.size()-1) equals end */ private final List<BetterBlockPos> path; private final List<Movement> movements; private final List<PathNode> nodes; private final Goal goal; private final int numNodes; private final CalculationContext context; private volatile boolean verified; Path(BetterBlockPos realStart, PathNode start, PathNode end, int numNodes, Goal goal, CalculationContext context) { this.end = new BetterBlockPos(end.x, end.y, end.z); this.numNodes = numNodes; this.movements = new ArrayList<>(); this.goal = goal; this.context = context; PathNode current = end; List<BetterBlockPos> tempPath = new ArrayList<>(); List<PathNode> tempNodes = new ArrayList<>(); while (current != null) { tempNodes.add(current); tempPath.add(new BetterBlockPos(current.x, current.y, current.z)); current = current.previous; } // If the position the player is at is different from the position we told A* to start from, // and A* gave us no movements, then add a fake node that will allow a movement to be created // that gets us to the single position in the path. // See PathingBehavior#createPathfinder and https://github.com/cabaletta/baritone/pull/4519 var startNodePos = new BetterBlockPos(start.x, start.y, start.z); if (!realStart.equals(startNodePos) && start.equals(end)) { this.start = realStart; PathNode fakeNode = new PathNode(realStart.x, realStart.y, realStart.z, goal); fakeNode.cost = 0; tempNodes.add(fakeNode); tempPath.add(realStart); } else { this.start = startNodePos; } // Nodes are traversed last to first so we need to reverse the list this.path = Lists.reverse(tempPath); this.nodes = Lists.reverse(tempNodes); } @Override public Goal getGoal() { return goal; } private boolean assembleMovements() { if (path.isEmpty() || !movements.isEmpty()) { throw new IllegalStateException("Path must not be empty"); } for (int i = 0; i < path.size() - 1; i++) { double cost = nodes.get(i + 1).cost - nodes.get(i).cost; Movement move = runBackwards(path.get(i), path.get(i + 1), cost); if (move == null) { return true; } else { movements.add(move); } } return false; } private Movement runBackwards(BetterBlockPos src, BetterBlockPos dest, double cost) { for (Moves moves : Moves.values()) { Movement move = moves.apply0(context, src); if (move.getDest().equals(dest)) { // have to calculate the cost at calculation time so we can accurately judge whether a cost increase happened between cached calculation and real execution // however, taking into account possible favoring that could skew the node cost, we really want the stricter limit of the two // so we take the minimum of the path node cost difference, and the calculated cost move.override(Math.min(move.calculateCost(context), cost)); return move; } } // this is no longer called from bestPathSoFar, now it's in postprocessing Helper.HELPER.logDebug("Movement became impossible during calculation " + src + " " + dest + " " + dest.subtract(src)); return null; } @Override public IPath postProcess() { if (verified) { throw new IllegalStateException("Path must not be verified twice"); } verified = true; boolean failed = assembleMovements(); movements.forEach(m -> m.checkLoadedChunk(context)); if (failed) { // at least one movement became impossible during calculation CutoffPath res = new CutoffPath(this, movements().size()); if (res.movements().size() != movements.size()) { throw new IllegalStateException("Path has wrong size after cutoff"); } return res; } // more post processing here sanityCheck(); return this; } @Override public List<IMovement> movements() { if (!verified) { // edge case note: this is called during verification throw new IllegalStateException("Path not yet verified"); } return Collections.unmodifiableList(movements); } @Override public List<BetterBlockPos> positions() { return Collections.unmodifiableList(path); } @Override public int getNumNodesConsidered() { return numNodes; } @Override public BetterBlockPos getSrc() { return start; } @Override public BetterBlockPos getDest() { return end; } } ================================================ FILE: src/main/java/baritone/pathing/calc/PathNode.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.ActionCosts; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; /** * A node in the path, containing the cost and steps to get to it. * * @author leijurv */ public final class PathNode { /** * The position of this node */ public final int x; public final int y; public final int z; /** * Cached, should always be equal to goal.heuristic(pos) */ public final double estimatedCostToGoal; /** * Total cost of getting from start to here * Mutable and changed by PathFinder */ public double cost; /** * Should always be equal to estimatedCosttoGoal + cost * Mutable and changed by PathFinder */ public double combinedCost; /** * In the graph search, what previous node contributed to the cost * Mutable and changed by PathFinder */ public PathNode previous; /** * Where is this node in the array flattenization of the binary heap? Needed for decrease-key operations. */ public int heapPosition; public PathNode(int x, int y, int z, Goal goal) { this.previous = null; this.cost = ActionCosts.COST_INF; this.estimatedCostToGoal = goal.heuristic(x, y, z); if (Double.isNaN(estimatedCostToGoal)) { throw new IllegalStateException(String.format( "%s calculated implausible heuristic NaN at %s %s %s", goal, SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z))); } this.heapPosition = -1; this.x = x; this.y = y; this.z = z; } public boolean isOpen() { return heapPosition != -1; } /** * TODO: Possibly reimplement hashCode and equals. They are necessary for this class to function but they could be done better * * @return The hash code value for this {@link PathNode} */ @Override public int hashCode() { return (int) BetterBlockPos.longHash(x, y, z); } @Override public boolean equals(Object obj) { // GOTTA GO FAST // ALL THESE CHECKS ARE FOR PEOPLE WHO WANT SLOW CODE // SKRT SKRT //if (obj == null || !(obj instanceof PathNode)) { // return false; //} final PathNode other = (PathNode) obj; //return Objects.equals(this.pos, other.pos) && Objects.equals(this.goal, other.goal); return x == other.x && y == other.y && z == other.z; } } ================================================ FILE: src/main/java/baritone/pathing/calc/openset/BinaryHeapOpenSet.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc.openset; import baritone.pathing.calc.PathNode; import java.util.Arrays; /** * A binary heap implementation of an open set. This is the one used in the AStarPathFinder. * * @author leijurv */ public final class BinaryHeapOpenSet implements IOpenSet { /** * The initial capacity of the heap (2^10) */ private static final int INITIAL_CAPACITY = 1024; /** * The array backing the heap */ private PathNode[] array; /** * The size of the heap */ private int size; public BinaryHeapOpenSet() { this(INITIAL_CAPACITY); } public BinaryHeapOpenSet(int size) { this.size = 0; this.array = new PathNode[size]; } public int size() { return size; } @Override public final void insert(PathNode value) { if (size >= array.length - 1) { array = Arrays.copyOf(array, array.length << 1); } size++; value.heapPosition = size; array[size] = value; update(value); } @Override public final void update(PathNode val) { int index = val.heapPosition; int parentInd = index >>> 1; double cost = val.combinedCost; PathNode parentNode = array[parentInd]; while (index > 1 && parentNode.combinedCost > cost) { array[index] = parentNode; array[parentInd] = val; val.heapPosition = parentInd; parentNode.heapPosition = index; index = parentInd; parentInd = index >>> 1; parentNode = array[parentInd]; } } @Override public final boolean isEmpty() { return size == 0; } @Override public final PathNode removeLowest() { if (size == 0) { throw new IllegalStateException("Cannot remove from empty heap"); } PathNode result = array[1]; PathNode val = array[size]; array[1] = val; val.heapPosition = 1; array[size] = null; size--; result.heapPosition = -1; if (size < 2) { return result; } int index = 1; int smallerChild = 2; double cost = val.combinedCost; do { PathNode smallerChildNode = array[smallerChild]; double smallerChildCost = smallerChildNode.combinedCost; if (smallerChild < size) { PathNode rightChildNode = array[smallerChild + 1]; double rightChildCost = rightChildNode.combinedCost; if (smallerChildCost > rightChildCost) { smallerChild++; smallerChildCost = rightChildCost; smallerChildNode = rightChildNode; } } if (cost <= smallerChildCost) { break; } array[index] = smallerChildNode; array[smallerChild] = val; val.heapPosition = smallerChild; smallerChildNode.heapPosition = index; index = smallerChild; } while ((smallerChild <<= 1) <= size); return result; } } ================================================ FILE: src/main/java/baritone/pathing/calc/openset/IOpenSet.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc.openset; import baritone.pathing.calc.PathNode; /** * An open set for A* or similar graph search algorithm * * @author leijurv */ public interface IOpenSet { /** * Inserts the specified node into the heap * * @param node The node */ void insert(PathNode node); /** * @return {@code true} if the heap has no elements; {@code false} otherwise. */ boolean isEmpty(); /** * Removes and returns the minimum element in the heap. * * @return The minimum element in the heap */ PathNode removeLowest(); /** * A faster path has been found to this node, decreasing its cost. Perform a decrease-key operation. * * @param node The node */ void update(PathNode node); } ================================================ FILE: src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc.openset; import baritone.pathing.calc.PathNode; /** * A linked list implementation of an open set. This is the original implementation from MineBot. * It has incredibly fast insert performance, at the cost of O(n) removeLowest. * It sucks. BinaryHeapOpenSet results in more than 10x more nodes considered in 4 seconds. * * @author leijurv */ class LinkedListOpenSet implements IOpenSet { private Node first = null; @Override public boolean isEmpty() { return first == null; } @Override public void insert(PathNode pathNode) { Node node = new Node(); node.val = pathNode; node.nextOpen = first; first = node; } @Override public void update(PathNode node) { } @Override public PathNode removeLowest() { if (first == null) { return null; } Node current = first.nextOpen; if (current == null) { Node n = first; first = null; return n.val; } Node previous = first; double bestValue = first.val.combinedCost; Node bestNode = first; Node beforeBest = null; while (current != null) { double comp = current.val.combinedCost; if (comp < bestValue) { bestValue = comp; bestNode = current; beforeBest = previous; } previous = current; current = current.nextOpen; } if (beforeBest == null) { first = first.nextOpen; bestNode.nextOpen = null; return bestNode.val; } beforeBest.nextOpen = bestNode.nextOpen; bestNode.nextOpen = null; return bestNode.val; } public static class Node { //wrapper with next private Node nextOpen; private PathNode val; } } ================================================ FILE: src/main/java/baritone/pathing/movement/CalculationContext.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.ActionCosts; import baritone.cache.WorldData; import baritone.pathing.precompute.PrecomputedData; import baritone.utils.BlockStateInterface; import baritone.utils.ToolSet; import baritone.utils.pathing.BetterWorldBorder; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import java.util.ArrayList; import java.util.List; import static baritone.api.pathing.movement.ActionCosts.COST_INF; /** * @author Brady * @since 8/7/2018 */ public class CalculationContext { private static final ItemStack STACK_BUCKET_WATER = new ItemStack(Items.WATER_BUCKET); public final boolean safeForThreadedUse; public final IBaritone baritone; public final Level world; public final WorldData worldData; public final BlockStateInterface bsi; public final ToolSet toolSet; public final boolean hasWaterBucket; public final boolean hasThrowaway; public final boolean canSprint; protected final double placeBlockCost; // protected because you should call the function instead public final boolean allowBreak; public final List<Block> allowBreakAnyway; public final boolean allowParkour; public final boolean allowParkourPlace; public final boolean allowJumpAtBuildLimit; public final boolean allowParkourAscend; public final boolean assumeWalkOnWater; public boolean allowFallIntoLava; public final int frostWalker; public final boolean allowDiagonalDescend; public final boolean allowDiagonalAscend; public final boolean allowDownward; public int minFallHeight; public int maxFallHeightNoWater; public final int maxFallHeightBucket; public final double waterWalkSpeed; public final double breakBlockAdditionalCost; public double backtrackCostFavoringCoefficient; public double jumpPenalty; public final double walkOnWaterOnePenalty; public final boolean allowWalkOnMagmaBlocks; public final BetterWorldBorder worldBorder; public final PrecomputedData precomputedData; public CalculationContext(IBaritone baritone) { this(baritone, false); } public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread) { this.precomputedData = new PrecomputedData(); this.safeForThreadedUse = forUseOnAnotherThread; this.baritone = baritone; LocalPlayer player = baritone.getPlayerContext().player(); this.world = baritone.getPlayerContext().world(); this.worldData = (WorldData) baritone.getPlayerContext().worldData(); this.bsi = new BlockStateInterface(baritone.getPlayerContext(), forUseOnAnotherThread); this.toolSet = new ToolSet(player); this.hasThrowaway = Baritone.settings().allowPlace.value && ((Baritone) baritone).getInventoryBehavior().hasGenericThrowaway(); this.hasWaterBucket = Baritone.settings().allowWaterBucketFall.value && Inventory.isHotbarSlot(player.getInventory().findSlotMatchingItem(STACK_BUCKET_WATER)) && world.dimension() != Level.NETHER; this.canSprint = Baritone.settings().allowSprint.value && player.getFoodData().getFoodLevel() > 6; this.placeBlockCost = Baritone.settings().blockPlacementPenalty.value; this.allowBreak = Baritone.settings().allowBreak.value; this.allowBreakAnyway = new ArrayList<>(Baritone.settings().allowBreakAnyway.value); this.allowParkour = Baritone.settings().allowParkour.value; this.allowParkourPlace = Baritone.settings().allowParkourPlace.value; this.allowJumpAtBuildLimit = Baritone.settings().allowJumpAtBuildLimit.value; this.allowParkourAscend = Baritone.settings().allowParkourAscend.value; this.assumeWalkOnWater = Baritone.settings().assumeWalkOnWater.value; this.allowFallIntoLava = false; // Super secret internal setting for ElytraBehavior this.frostWalker = EnchantmentHelper.getEnchantmentLevel(Enchantments.FROST_WALKER, baritone.getPlayerContext().player()); this.allowDiagonalDescend = Baritone.settings().allowDiagonalDescend.value; this.allowDiagonalAscend = Baritone.settings().allowDiagonalAscend.value; this.allowDownward = Baritone.settings().allowDownward.value; this.minFallHeight = 3; // Minimum fall height used by MovementFall this.maxFallHeightNoWater = Baritone.settings().maxFallHeightNoWater.value; this.maxFallHeightBucket = Baritone.settings().maxFallHeightBucket.value; int depth = EnchantmentHelper.getDepthStrider(player); if (depth > 3) { depth = 3; } float mult = depth / 3.0F; this.waterWalkSpeed = ActionCosts.WALK_ONE_IN_WATER_COST * (1 - mult) + ActionCosts.WALK_ONE_BLOCK_COST * mult; this.breakBlockAdditionalCost = Baritone.settings().blockBreakAdditionalPenalty.value; this.backtrackCostFavoringCoefficient = Baritone.settings().backtrackCostFavoringCoefficient.value; this.jumpPenalty = Baritone.settings().jumpPenalty.value; this.walkOnWaterOnePenalty = Baritone.settings().walkOnWaterOnePenalty.value; this.allowWalkOnMagmaBlocks = Baritone.settings().allowWalkOnMagmaBlocks.value; // why cache these things here, why not let the movements just get directly from settings? // because if some movements are calculated one way and others are calculated another way, // then you get a wildly inconsistent path that isn't optimal for either scenario. this.worldBorder = new BetterWorldBorder(world.getWorldBorder()); } public final IBaritone getBaritone() { return baritone; } public BlockState get(int x, int y, int z) { return bsi.get0(x, y, z); // laughs maniacally } public boolean isLoaded(int x, int z) { return bsi.isLoaded(x, z); } public BlockState get(BlockPos pos) { return get(pos.getX(), pos.getY(), pos.getZ()); } public Block getBlock(int x, int y, int z) { return get(x, y, z).getBlock(); } public double costOfPlacingAt(int x, int y, int z, BlockState current) { if (!hasThrowaway) { // only true if allowPlace is true, see constructor return COST_INF; } if (isPossiblyProtected(x, y, z)) { return COST_INF; } if (!worldBorder.canPlaceAt(x, z)) { return COST_INF; } if (!Baritone.settings().allowPlaceInFluidsSource.value && current.getFluidState().isSource()) { return COST_INF; } if (!Baritone.settings().allowPlaceInFluidsFlow.value && !current.getFluidState().isEmpty() && !current.getFluidState().isSource()) { return COST_INF; } return placeBlockCost; } public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { if (!allowBreak && !allowBreakAnyway.contains(current.getBlock())) { return COST_INF; } if (isPossiblyProtected(x, y, z)) { return COST_INF; } return 1; } public double placeBucketCost() { return placeBlockCost; // shrug } public boolean isPossiblyProtected(int x, int y, int z) { // TODO more protection logic here; see #220 return false; } } ================================================ FILE: src/main/java/baritone/pathing/movement/Movement.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.IMovement; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.behavior.PathingBehavior; import baritone.utils.BlockStateInterface; import java.util.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.phys.AABB; public abstract class Movement implements IMovement, MovementHelper { public static final Direction[] HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.DOWN}; protected final IBaritone baritone; protected final IPlayerContext ctx; private MovementState currentState = new MovementState().setStatus(MovementStatus.PREPPING); protected final BetterBlockPos src; protected final BetterBlockPos dest; /** * The positions that need to be broken before this movement can ensue */ protected final BetterBlockPos[] positionsToBreak; /** * The position where we need to place a block before this movement can ensue */ protected final BetterBlockPos positionToPlace; private Double cost; public List<BlockPos> toBreakCached = null; public List<BlockPos> toPlaceCached = null; public List<BlockPos> toWalkIntoCached = null; private Set<BetterBlockPos> validPositionsCached = null; private Boolean calculatedWhileLoaded; protected Movement(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest, BetterBlockPos[] toBreak, BetterBlockPos toPlace) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); this.src = src; this.dest = dest; this.positionsToBreak = toBreak; this.positionToPlace = toPlace; } protected Movement(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest, BetterBlockPos[] toBreak) { this(baritone, src, dest, toBreak, null); } public double getCost() throws NullPointerException { return cost; } public double getCost(CalculationContext context) { if (cost == null) { cost = calculateCost(context); } return cost; } public abstract double calculateCost(CalculationContext context); public double recalculateCost(CalculationContext context) { cost = null; return getCost(context); } public void override(double cost) { this.cost = cost; } protected abstract Set<BetterBlockPos> calculateValidPositions(); public Set<BetterBlockPos> getValidPositions() { if (validPositionsCached == null) { validPositionsCached = calculateValidPositions(); Objects.requireNonNull(validPositionsCached); } return validPositionsCached; } protected boolean playerInValidPosition() { return getValidPositions().contains(ctx.playerFeet()) || getValidPositions().contains(((PathingBehavior) baritone.getPathingBehavior()).pathStart()); } /** * Handles the execution of the latest Movement * State, and offers a Status to the calling class. * * @return Status */ @Override public MovementStatus update() { ctx.player().getAbilities().flying = false; currentState = updateState(currentState); if (MovementHelper.isLiquid(ctx, ctx.playerFeet()) && ctx.player().position().y < dest.y + 0.6) { currentState.setInput(Input.JUMP, true); } if (ctx.player().isInWall()) { ctx.getSelectedBlock().ifPresent(pos -> MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, pos))); currentState.setInput(Input.CLICK_LEFT, true); } // If the movement target has to force the new rotations, or we aren't using silent move, then force the rotations currentState.getTarget().getRotation().ifPresent(rotation -> baritone.getLookBehavior().updateTarget( rotation, currentState.getTarget().hasToForceRotations())); baritone.getInputOverrideHandler().clearAllKeys(); currentState.getInputStates().forEach((input, forced) -> { baritone.getInputOverrideHandler().setInputForceState(input, forced); }); currentState.getInputStates().clear(); // If the current status indicates a completed movement if (currentState.getStatus().isComplete()) { baritone.getInputOverrideHandler().clearAllKeys(); } return currentState.getStatus(); } protected boolean prepared(MovementState state) { if (state.getStatus() == MovementStatus.WAITING) { return true; } boolean somethingInTheWay = false; for (BetterBlockPos blockPos : positionsToBreak) { if (!ctx.world().getEntitiesOfClass(FallingBlockEntity.class, new AABB(0, 0, 0, 1, 1.1, 1).move(blockPos)).isEmpty() && Baritone.settings().pauseMiningForFallingBlocks.value) { return false; } if (!MovementHelper.canWalkThrough(ctx, blockPos)) { // can't break air, so don't try somethingInTheWay = true; MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); Optional<Rotation> reachable = RotationUtils.reachable(ctx, blockPos, ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { Rotation rotTowardsBlock = reachable.get(); state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true)); if (ctx.isLookingAt(blockPos) || ctx.playerRotations().isReallyCloseTo(rotTowardsBlock)) { state.setInput(Input.CLICK_LEFT, true); } return false; } //get rekt minecraft //i'm doing it anyway //i dont care if theres snow in the way!!!!!!! //you dont own me!!!! state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(blockPos), ctx.playerRotations()), true) ); // don't check selectedblock on this one, this is a fallback when we can't see any face directly, it's intended to be breaking the "incorrect" block state.setInput(Input.CLICK_LEFT, true); return false; } } if (somethingInTheWay) { // There's a block or blocks that we can't walk through, but we have no target rotation to reach any // So don't return true, actually set state to unreachable state.setStatus(MovementStatus.UNREACHABLE); return true; } return true; } @Override public boolean safeToCancel() { return safeToCancel(currentState); } protected boolean safeToCancel(MovementState currentState) { return true; } @Override public BetterBlockPos getSrc() { return src; } @Override public BetterBlockPos getDest() { return dest; } @Override public void reset() { currentState = new MovementState().setStatus(MovementStatus.PREPPING); } /** * Calculate latest movement state. Gets called once a tick. * * @param state The current state * @return The new state */ public MovementState updateState(MovementState state) { if (!prepared(state)) { return state.setStatus(MovementStatus.PREPPING); } else if (state.getStatus() == MovementStatus.PREPPING) { state.setStatus(MovementStatus.WAITING); } if (state.getStatus() == MovementStatus.WAITING) { state.setStatus(MovementStatus.RUNNING); } return state; } @Override public BlockPos getDirection() { return getDest().subtract(getSrc()); } public void checkLoadedChunk(CalculationContext context) { calculatedWhileLoaded = context.bsi.worldContainsLoadedChunk(dest.x, dest.z); } @Override public boolean calculatedWhileLoaded() { return calculatedWhileLoaded; } @Override public void resetBlockCache() { toBreakCached = null; toPlaceCached = null; toWalkIntoCached = null; } public List<BlockPos> toBreak(BlockStateInterface bsi) { if (toBreakCached != null) { return toBreakCached; } List<BlockPos> result = new ArrayList<>(); for (BetterBlockPos positionToBreak : positionsToBreak) { if (!MovementHelper.canWalkThrough(bsi, positionToBreak.x, positionToBreak.y, positionToBreak.z)) { result.add(positionToBreak); } } toBreakCached = result; return result; } public List<BlockPos> toPlace(BlockStateInterface bsi) { if (toPlaceCached != null) { return toPlaceCached; } List<BlockPos> result = new ArrayList<>(); if (positionToPlace != null && !MovementHelper.canWalkOn(bsi, positionToPlace.x, positionToPlace.y, positionToPlace.z)) { result.add(positionToPlace); } toPlaceCached = result; return result; } public List<BlockPos> toWalkInto(BlockStateInterface bsi) { // overridden by movementdiagonal if (toWalkIntoCached == null) { toWalkIntoCached = new ArrayList<>(); } return toWalkIntoCached; } public BlockPos[] toBreakAll() { return positionsToBreak; } } ================================================ FILE: src/main/java/baritone/pathing/movement/MovementHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.pathing.movement.ActionCosts; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.*; import baritone.api.utils.Rotation; import baritone.api.utils.input.Input; import baritone.pathing.movement.MovementState.MovementTarget; import baritone.pathing.precompute.Ternary; import baritone.utils.BlockStateInterface; import baritone.utils.ToolSet; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.piston.MovingPistonBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.SlabType; import net.minecraft.world.level.block.state.properties.StairsShape; import net.minecraft.world.level.material.FlowingFluid; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Material; import net.minecraft.world.level.material.WaterFluid; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import java.util.*; import static baritone.api.utils.RotationUtils.DEG_TO_RAD_F; import static baritone.pathing.movement.Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP; import static baritone.pathing.precompute.Ternary.*; /** * Static helpers for cost calculation * * @author leijurv */ public interface MovementHelper extends ActionCosts, Helper { static boolean avoidBreaking(BlockStateInterface bsi, int x, int y, int z, BlockState state) { if (!bsi.worldBorder.canPlaceAt(x, z)) { return true; } Block b = state.getBlock(); return Baritone.settings().blocksToDisallowBreaking.value.contains(b) || b == Blocks.ICE // ice becomes water, and water can mess up the path || b instanceof InfestedBlock // obvious reasons // call context.get directly with x,y,z. no need to make 5 new BlockPos for no reason || avoidAdjacentBreaking(bsi, x, y + 1, z, true) || avoidAdjacentBreaking(bsi, x + 1, y, z, false) || avoidAdjacentBreaking(bsi, x - 1, y, z, false) || avoidAdjacentBreaking(bsi, x, y, z + 1, false) || avoidAdjacentBreaking(bsi, x, y, z - 1, false); } static boolean avoidAdjacentBreaking(BlockStateInterface bsi, int x, int y, int z, boolean directlyAbove) { // returns true if you should avoid breaking a block that's adjacent to this one (e.g. lava that will start flowing if you give it a path) // this is only called for north, south, east, west, and up. this is NOT called for down. // we assume that it's ALWAYS okay to break the block thats ABOVE liquid BlockState state = bsi.get0(x, y, z); Block block = state.getBlock(); if (!directlyAbove // it is fine to mine a block that has a falling block directly above, this (the cost of breaking the stacked fallings) is included in cost calculations // therefore if directlyAbove is true, we will actually ignore if this is falling && block instanceof FallingBlock // obviously, this check is only valid for falling blocks && Baritone.settings().avoidUpdatingFallingBlocks.value // and if the setting is enabled && FallingBlock.isFree(bsi.get0(x, y - 1, z))) { // and if it would fall (i.e. it's unsupported) return true; // dont break a block that is adjacent to unsupported gravel because it can cause really weird stuff } // only pure liquids for now // waterlogged blocks can have closed bottom sides and such if (block instanceof LiquidBlock) { if (directlyAbove || Baritone.settings().strictLiquidCheck.value) { return true; } int level = state.getValue(LiquidBlock.LEVEL); if (level == 0) { return true; // source blocks like to flow horizontally } // everything else will prefer flowing down return !(bsi.get0(x, y - 1, z).getBlock() instanceof LiquidBlock); // assume everything is in a static state } return !state.getFluidState().isEmpty(); } static boolean canWalkThrough(IPlayerContext ctx, BetterBlockPos pos) { return canWalkThrough(new BlockStateInterface(ctx), pos.x, pos.y, pos.z); } static boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z) { return canWalkThrough(bsi, x, y, z, bsi.get0(x, y, z)); } static boolean canWalkThrough(CalculationContext context, int x, int y, int z, BlockState state) { return context.precomputedData.canWalkThrough(context.bsi, x, y, z, state); } static boolean canWalkThrough(CalculationContext context, int x, int y, int z) { return context.precomputedData.canWalkThrough(context.bsi, x, y, z, context.get(x, y, z)); } static boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, BlockState state) { Ternary canWalkThrough = canWalkThroughBlockState(state); if (canWalkThrough == YES) { return true; } if (canWalkThrough == NO) { return false; } return canWalkThroughPosition(bsi, x, y, z, state); } static Ternary canWalkThroughBlockState(BlockState state) { Block block = state.getBlock(); if (block instanceof AirBlock) { return YES; } if (block instanceof BaseFireBlock || block == Blocks.COBWEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof AbstractSkullBlock || block == Blocks.BUBBLE_COLUMN || block instanceof ShulkerBoxBlock || block instanceof SlabBlock || block instanceof TrapDoorBlock || block == Blocks.HONEY_BLOCK || block == Blocks.END_ROD || block == Blocks.SWEET_BERRY_BUSH || block == Blocks.POINTED_DRIPSTONE || block instanceof AmethystClusterBlock || block instanceof AzaleaBlock) { return NO; } if (block == Blocks.BIG_DRIPLEAF) { return NO; } if (block == Blocks.POWDER_SNOW) { return NO; } if (Baritone.settings().blocksToAvoid.value.contains(block)) { return NO; } if (block instanceof DoorBlock || block instanceof FenceGateBlock) { // TODO this assumes that all doors in all mods are openable if (block == Blocks.IRON_DOOR) { return NO; } return YES; } if (block instanceof CarpetBlock) { return MAYBE; } if (block instanceof SnowLayerBlock) { // snow layers cached as the top layer of a packed chunk have no metadata, we can't make a decision based on their depth here // it would otherwise make long distance pathing through snowy biomes impossible return MAYBE; } FluidState fluidState = state.getFluidState(); if (!fluidState.isEmpty()) { if (fluidState.getType().getAmount(fluidState) != 8) { return NO; } else { return MAYBE; } } if (block instanceof CauldronBlock) { return NO; } try { // A dodgy catch-all at the end, for most blocks with default behaviour this will work, however where blocks are special this will error out, and we can handle it when we have this information if (state.isPathfindable(null, null, PathComputationType.LAND)) { return YES; } else { return NO; } } catch (Throwable exception) { System.out.println("The block " + state.getBlock().getName().getString() + " requires a special case due to the exception " + exception.getMessage()); return MAYBE; } } static boolean canWalkThroughPosition(BlockStateInterface bsi, int x, int y, int z, BlockState state) { Block block = state.getBlock(); if (block instanceof CarpetBlock) { return canWalkOn(bsi, x, y - 1, z); } if (block instanceof SnowLayerBlock) { // if they're cached as a top block, we don't know their metadata // default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible) if (!bsi.worldContainsLoadedChunk(x, z)) { return true; } // the check in BlockSnow.isPassable is layers < 5 // while actually, we want < 3 because 3 or greater makes it impassable in a 2 high ceiling if (state.getValue(SnowLayerBlock.LAYERS) >= 3) { return false; } // ok, it's low enough we could walk through it, but is it supported? return canWalkOn(bsi, x, y - 1, z); } FluidState fluidState = state.getFluidState(); if (!fluidState.isEmpty()) { if (isFlowing(x, y, z, state, bsi)) { return false; } // Everything after this point has to be a special case as it relies on the water not being flowing, which means a special case is needed. if (Baritone.settings().assumeWalkOnWater.value) { return false; } BlockState up = bsi.get0(x, y + 1, z); if (!up.getFluidState().isEmpty() || up.getBlock() instanceof WaterlilyBlock) { return false; } return fluidState.getType() instanceof WaterFluid; } // every block that overrides isPassable with anything more complicated than a "return true;" or "return false;" // has already been accounted for above // therefore it's safe to not construct a blockpos from our x, y, z ints and instead just pass null return state.isPathfindable(bsi.access, BlockPos.ZERO, PathComputationType.LAND); // workaround for future compatibility =P } static Ternary fullyPassableBlockState(BlockState state) { Block block = state.getBlock(); if (block instanceof AirBlock) { // early return for most common case return YES; } // exceptions - blocks that are isPassable true, but we can't actually jump through if (block instanceof BaseFireBlock || block == Blocks.TRIPWIRE || block == Blocks.COBWEB || block == Blocks.VINE || block == Blocks.LADDER || block == Blocks.COCOA || block instanceof AzaleaBlock || block instanceof DoorBlock || block instanceof FenceGateBlock || block instanceof SnowLayerBlock || !state.getFluidState().isEmpty() || block instanceof TrapDoorBlock || block instanceof EndPortalBlock || block instanceof SkullBlock || block instanceof ShulkerBoxBlock) { return NO; } // door, fence gate, liquid, trapdoor have been accounted for, nothing else uses the world or pos parameters // at least in 1.12.2 vanilla, that is..... try { // A dodgy catch-all at the end, for most blocks with default behaviour this will work, however where blocks are special this will error out, and we can handle it when we have this information if (state.isPathfindable(null, null, PathComputationType.LAND)) { return YES; } else { return NO; } } catch (Throwable exception) { // see PR #1087 for why System.out.println("The block " + state.getBlock().getName().getString() + " requires a special case due to the exception " + exception.getMessage()); return MAYBE; } } /** * canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click), * not including water, and not including ladders or vines or cobwebs (they slow us down) */ static boolean fullyPassable(CalculationContext context, int x, int y, int z) { return fullyPassable(context, x, y, z, context.get(x, y, z)); } static boolean fullyPassable(CalculationContext context, int x, int y, int z, BlockState state) { return context.precomputedData.fullyPassable(context.bsi, x, y, z, state); } static boolean fullyPassable(IPlayerContext ctx, BlockPos pos) { BlockState state = ctx.world().getBlockState(pos); Ternary fullyPassable = fullyPassableBlockState(state); if (fullyPassable == YES) { return true; } if (fullyPassable == NO) { return false; } return fullyPassablePosition(new BlockStateInterface(ctx), pos.getX(), pos.getY(), pos.getZ(), state); // meh } static boolean fullyPassablePosition(BlockStateInterface bsi, int x, int y, int z, BlockState state) { return state.isPathfindable(bsi.access, bsi.isPassableBlockPos.set(x, y, z), PathComputationType.LAND); } static boolean isReplaceable(int x, int y, int z, BlockState state, BlockStateInterface bsi) { // for MovementTraverse and MovementAscend // block double plant defaults to true when the block doesn't match, so don't need to check that case // all other overrides just return true or false // the only case to deal with is snow /* * public boolean isReplaceable(IBlockAccess worldIn, BlockPos pos) * { * return ((Integer)worldIn.getBlockState(pos).getValue(LAYERS)).intValue() == 1; * } */ Block block = state.getBlock(); if (block instanceof AirBlock) { // early return for common cases hehe return true; } if (block instanceof SnowLayerBlock) { // as before, default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible) if (!bsi.worldContainsLoadedChunk(x, z)) { return true; } return state.getValue(SnowLayerBlock.LAYERS) == 1; } if (block == Blocks.LARGE_FERN || block == Blocks.TALL_GRASS) { return true; } return state.getMaterial().isReplaceable(); } @Deprecated static boolean isReplacable(int x, int y, int z, BlockState state, BlockStateInterface bsi) { return isReplaceable(x, y, z, state, bsi); } static boolean isDoorPassable(IPlayerContext ctx, BlockPos doorPos, BlockPos playerPos) { if (playerPos.equals(doorPos)) { return false; } BlockState state = BlockStateInterface.get(ctx, doorPos); if (!(state.getBlock() instanceof DoorBlock)) { return true; } return isHorizontalBlockPassable(doorPos, state, playerPos, DoorBlock.OPEN); } static boolean isGatePassable(IPlayerContext ctx, BlockPos gatePos, BlockPos playerPos) { if (playerPos.equals(gatePos)) { return false; } BlockState state = BlockStateInterface.get(ctx, gatePos); if (!(state.getBlock() instanceof FenceGateBlock)) { return true; } return state.getValue(FenceGateBlock.OPEN); } static boolean isHorizontalBlockPassable(BlockPos blockPos, BlockState blockState, BlockPos playerPos, BooleanProperty propertyOpen) { if (playerPos.equals(blockPos)) { return false; } Direction.Axis facing = blockState.getValue(HorizontalDirectionalBlock.FACING).getAxis(); boolean open = blockState.getValue(propertyOpen); Direction.Axis playerFacing; if (playerPos.north().equals(blockPos) || playerPos.south().equals(blockPos)) { playerFacing = Direction.Axis.Z; } else if (playerPos.east().equals(blockPos) || playerPos.west().equals(blockPos)) { playerFacing = Direction.Axis.X; } else { return true; } return (facing == playerFacing) == open; } static boolean avoidWalkingInto(BlockState state) { Block block = state.getBlock(); return !state.getFluidState().isEmpty() || (block == Blocks.MAGMA_BLOCK && !Baritone.settings().allowWalkOnMagmaBlocks.value) || block == Blocks.CACTUS || block == Blocks.SWEET_BERRY_BUSH || block instanceof BaseFireBlock || block == Blocks.END_PORTAL || block == Blocks.COBWEB || block == Blocks.BUBBLE_COLUMN; } /** * Can I walk on this block without anything weird happening like me falling * through? Includes water because we know that we automatically jump on * water * <p> * If changing something in this function remember to also change it in precomputed data * * @param bsi Block state provider * @param x The block's x position * @param y The block's y position * @param z The block's z position * @param state The state of the block at the specified location * @return Whether or not the specified block can be walked on */ static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, BlockState state) { Ternary canWalkOn = canWalkOnBlockState(state); if (canWalkOn == YES) { return true; } if (canWalkOn == NO) { return false; } return canWalkOnPosition(bsi, x, y, z, state); } static Ternary canWalkOnBlockState(BlockState state) { Block block = state.getBlock(); if (isBlockNormalCube(state) && (block != Blocks.MAGMA_BLOCK || Baritone.settings().allowWalkOnMagmaBlocks.value) && block != Blocks.BUBBLE_COLUMN && block != Blocks.HONEY_BLOCK) { return YES; } if (block instanceof AzaleaBlock) { return YES; } if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.value)) { // TODO reconsider this return YES; } if (block == Blocks.FARMLAND || block == Blocks.DIRT_PATH || block == Blocks.SOUL_SAND) { return YES; } if (block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST) { return YES; } if (block == Blocks.GLASS || block instanceof StainedGlassBlock) { return YES; } if (block instanceof StairBlock) { return YES; } if (isWater(state)) { return MAYBE; } if (MovementHelper.isLava(state) && Baritone.settings().assumeWalkOnLava.value) { return MAYBE; } if (block instanceof SlabBlock) { if (!Baritone.settings().allowWalkOnBottomSlab.value) { if (state.getValue(SlabBlock.TYPE) != SlabType.BOTTOM) { return YES; } return NO; } return YES; } return NO; } static boolean canWalkOnPosition(BlockStateInterface bsi, int x, int y, int z, BlockState state) { Block block = state.getBlock(); if (isWater(state)) { // since this is called literally millions of times per second, the benefit of not allocating millions of useless "pos.up()" // BlockPos s that we'd just garbage collect immediately is actually noticeable. I don't even think its a decrease in readability BlockState upState = bsi.get0(x, y + 1, z); Block up = upState.getBlock(); if (up == Blocks.LILY_PAD || up instanceof CarpetBlock) { return true; } if (MovementHelper.isFlowing(x, y, z, state, bsi) || upState.getFluidState().getType() == Fluids.FLOWING_WATER) { // the only scenario in which we can walk on flowing water is if it's under still water with jesus off return isWater(upState) && !Baritone.settings().assumeWalkOnWater.value; } // if assumeWalkOnWater is on, we can only walk on water if there isn't water above it // if assumeWalkOnWater is off, we can only walk on water if there is water above it return isWater(upState) ^ Baritone.settings().assumeWalkOnWater.value; } if (MovementHelper.isLava(state) && !MovementHelper.isFlowing(x, y, z, state, bsi) && Baritone.settings().assumeWalkOnLava.value) { // if we get here it means that assumeWalkOnLava must be true, so put it last return true; } return false; // If we don't recognise it then we want to just return false to be safe. } static boolean canWalkOn(CalculationContext context, int x, int y, int z, BlockState state) { return context.precomputedData.canWalkOn(context.bsi, x, y, z, state); } static boolean canWalkOn(CalculationContext context, int x, int y, int z) { return canWalkOn(context, x, y, z, context.get(x, y, z)); } static boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos, BlockState state) { return canWalkOn(new BlockStateInterface(ctx), pos.x, pos.y, pos.z, state); } static boolean canWalkOn(IPlayerContext ctx, BlockPos pos) { return canWalkOn(new BlockStateInterface(ctx), pos.getX(), pos.getY(), pos.getZ()); } static boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos) { return canWalkOn(new BlockStateInterface(ctx), pos.x, pos.y, pos.z); } static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z) { return canWalkOn(bsi, x, y, z, bsi.get0(x, y, z)); } static boolean canUseFrostWalker(CalculationContext context, BlockState state) { return context.frostWalker != 0 && state.getMaterial() == Material.WATER && ((Integer) state.getValue(LiquidBlock.LEVEL)) == 0; } static boolean canUseFrostWalker(IPlayerContext ctx, BlockPos pos) { BlockState state = BlockStateInterface.get(ctx, pos); return EnchantmentHelper.hasFrostWalker(ctx.player()) && state.getMaterial() == Material.WATER && ((Integer) state.getValue(LiquidBlock.LEVEL)) == 0; } /** * If movements make us stand/walk on this block, will it have a top to walk on? */ static boolean mustBeSolidToWalkOn(CalculationContext context, int x, int y, int z, BlockState state) { Block block = state.getBlock(); if (block == Blocks.LADDER || block == Blocks.VINE) { return false; } if (!state.getFluidState().isEmpty()) { // used for frostwalker so only includes blocks where we are still on ground when leaving them to any side if (block instanceof SlabBlock) { if (state.getValue(SlabBlock.TYPE) != SlabType.BOTTOM) { return true; } } else if (block instanceof StairBlock) { if (state.getValue(StairBlock.HALF) == Half.TOP) { return true; } StairsShape shape = state.getValue(StairBlock.SHAPE); if (shape == StairsShape.INNER_LEFT || shape == StairsShape.INNER_RIGHT) { return true; } } else if (block instanceof TrapDoorBlock) { if (!state.getValue(TrapDoorBlock.OPEN) && state.getValue(TrapDoorBlock.HALF) == Half.TOP) { return true; } } else if (block == Blocks.SCAFFOLDING) { return true; } else if (block instanceof LeavesBlock) { return true; } if (context.assumeWalkOnWater) { return false; } Block blockAbove = context.getBlock(x, y + 1, z); if (blockAbove instanceof LiquidBlock) { return false; } } return true; } static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z) { return canPlaceAgainst(bsi, x, y, z, bsi.get0(x, y, z)); } static boolean canPlaceAgainst(BlockStateInterface bsi, BlockPos pos) { return canPlaceAgainst(bsi, pos.getX(), pos.getY(), pos.getZ()); } static boolean canPlaceAgainst(IPlayerContext ctx, BlockPos pos) { return canPlaceAgainst(new BlockStateInterface(ctx), pos); } static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z, BlockState state) { if (!bsi.worldBorder.canPlaceAt(x, z)) { return false; } // can we look at the center of a side face of this block and likely be able to place? // (thats how this check is used) // therefore dont include weird things that we technically could place against (like carpet) but practically can't return isBlockNormalCube(state) || state.getBlock() == Blocks.GLASS || state.getBlock() instanceof StainedGlassBlock; } static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, boolean includeFalling) { return getMiningDurationTicks(context, x, y, z, context.get(x, y, z), includeFalling); } static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, BlockState state, boolean includeFalling) { Block block = state.getBlock(); if (!canWalkThrough(context, x, y, z, state)) { if (!state.getFluidState().isEmpty()) { return COST_INF; } double mult = context.breakCostMultiplierAt(x, y, z, state); if (mult >= COST_INF) { return COST_INF; } if (avoidBreaking(context.bsi, x, y, z, state)) { return COST_INF; } double strVsBlock = context.toolSet.getStrVsBlock(state); if (strVsBlock <= 0) { return COST_INF; } double result = 1 / strVsBlock; result += context.breakBlockAdditionalCost; result *= mult; if (includeFalling) { BlockState above = context.get(x, y + 1, z); if (above.getBlock() instanceof FallingBlock) { result += getMiningDurationTicks(context, x, y + 1, z, above, true); } } return result; } return 0; // we won't actually mine it, so don't check fallings above } static boolean isBottomSlab(BlockState state) { return state.getBlock() instanceof SlabBlock && state.getValue(SlabBlock.TYPE) == SlabType.BOTTOM; } /** * AutoTool for a specific block * * @param ctx The player context * @param b the blockstate to mine */ static void switchToBestToolFor(IPlayerContext ctx, BlockState b) { switchToBestToolFor(ctx, b, new ToolSet(ctx.player()), BaritoneAPI.getSettings().preferSilkTouch.value); } /** * AutoTool for a specific block with precomputed ToolSet data * * @param ctx The player context * @param b the blockstate to mine * @param ts previously calculated ToolSet */ static void switchToBestToolFor(IPlayerContext ctx, BlockState b, ToolSet ts, boolean preferSilkTouch) { if (Baritone.settings().autoTool.value && !Baritone.settings().assumeExternalAutoTool.value) { ctx.player().getInventory().selected = ts.getBestSlot(b.getBlock(), preferSilkTouch); } } static void moveTowards(IPlayerContext ctx, MovementState state, BlockPos pos) { state.setTarget(new MovementTarget( RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(pos), ctx.playerRotations()).withPitch(ctx.playerRotations().getPitch()), false )).setInput(Input.MOVE_FORWARD, true); } static void moveTowardsWithoutRotation(IPlayerContext ctx, MovementState state, float idealYaw) { MovementOption.getOptions( Mth.sin(ctx.playerRotations().getYaw() * DEG_TO_RAD_F), Mth.cos(ctx.playerRotations().getYaw() * DEG_TO_RAD_F), Baritone.settings().allowSprint.value ).min(Comparator.comparing(option -> option.distanceToSq( Mth.sin(idealYaw * DEG_TO_RAD_F), Mth.cos(idealYaw * DEG_TO_RAD_F) ))).ifPresent(selection -> selection.setInputs(state)); } static void moveTowardsWithoutRotation(IPlayerContext ctx, MovementState state, BlockPos dest) { float idealYaw = RotationUtils.calcRotationFromVec3d( ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations() ).getYaw(); moveTowardsWithoutRotation(ctx, state, idealYaw); } static void moveTowardsWithSlightRotation(IPlayerContext ctx, MovementState state, BlockPos dest) { float idealYaw = RotationUtils.calcRotationFromVec3d( ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations() ).getYaw(); float distance = Rotation.yawDistanceFromOffset(ctx.playerRotations().getYaw(), idealYaw) % 45f; float newYaw = distance > 0f ? distance > 22.5f ? distance - 45f : distance : distance < -22.5f ? distance + 45f : distance; state.setTarget(new MovementTarget(new Rotation( ctx.playerRotations().getYaw() - newYaw, ctx.playerRotations().getPitch() ), true)); moveTowardsWithoutRotation(ctx, state, idealYaw); } /** * Returns whether or not the specified block is * water, regardless of whether or not it is flowing. * * @param state The block state * @return Whether or not the block is water */ static boolean isWater(BlockState state) { Fluid f = state.getFluidState().getType(); return f == Fluids.WATER || f == Fluids.FLOWING_WATER; } /** * Returns whether or not the block at the specified pos is * water, regardless of whether or not it is flowing. * * @param ctx The player context * @param bp The block pos * @return Whether or not the block is water */ static boolean isWater(IPlayerContext ctx, BlockPos bp) { return isWater(BlockStateInterface.get(ctx, bp)); } static boolean isLava(BlockState state) { Fluid f = state.getFluidState().getType(); return f == Fluids.LAVA || f == Fluids.FLOWING_LAVA; } /** * Returns whether or not the specified pos has a liquid * * @param ctx The player context * @param p The pos * @return Whether or not the block is a liquid */ static boolean isLiquid(IPlayerContext ctx, BlockPos p) { return isLiquid(BlockStateInterface.get(ctx, p)); } static boolean isLiquid(BlockState blockState) { return !blockState.getFluidState().isEmpty(); } static boolean possiblyFlowing(BlockState state) { FluidState fluidState = state.getFluidState(); return fluidState.getType() instanceof FlowingFluid && fluidState.getType().getAmount(fluidState) != 8; } static boolean isFlowing(int x, int y, int z, BlockState state, BlockStateInterface bsi) { FluidState fluidState = state.getFluidState(); if (!(fluidState.getType() instanceof FlowingFluid)) { return false; } if (fluidState.getType().getAmount(fluidState) != 8) { return true; } return possiblyFlowing(bsi.get0(x + 1, y, z)) || possiblyFlowing(bsi.get0(x - 1, y, z)) || possiblyFlowing(bsi.get0(x, y, z + 1)) || possiblyFlowing(bsi.get0(x, y, z - 1)); } static boolean isBlockNormalCube(BlockState state) { Block block = state.getBlock(); if (block instanceof BambooStalkBlock || block instanceof MovingPistonBlock || block instanceof ScaffoldingBlock || block instanceof ShulkerBoxBlock || block instanceof PointedDripstoneBlock || block instanceof AmethystClusterBlock) { return false; } try { return Block.isShapeFullBlock(state.getCollisionShape(null, null)); } catch (Exception ignored) { // if we can't get the collision shape, assume it's bad and add to blocksToAvoid } return false; } static PlaceResult attemptToPlaceABlock(MovementState state, IBaritone baritone, BlockPos placeAt, boolean preferDown, boolean wouldSneak) { IPlayerContext ctx = baritone.getPlayerContext(); Optional<Rotation> direct = RotationUtils.reachable(ctx, placeAt, wouldSneak); // we assume that if there is a block there, it must be replacable boolean found = false; if (direct.isPresent()) { state.setTarget(new MovementTarget(direct.get(), true)); found = true; } for (int i = 0; i < 5; i++) { BlockPos against1 = placeAt.relative(HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i]); if (MovementHelper.canPlaceAgainst(ctx, against1)) { if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(false, placeAt.getX(), placeAt.getY(), placeAt.getZ())) { // get ready to place a throwaway block Helper.HELPER.logDebug("bb pls get me some blocks. dirt, netherrack, cobble"); state.setStatus(MovementStatus.UNREACHABLE); return PlaceResult.NO_OPTION; } double faceX = (placeAt.getX() + against1.getX() + 1.0D) * 0.5D; double faceY = (placeAt.getY() + against1.getY() + 0.5D) * 0.5D; double faceZ = (placeAt.getZ() + against1.getZ() + 1.0D) * 0.5D; Rotation place = RotationUtils.calcRotationFromVec3d(wouldSneak ? RayTraceUtils.inferSneakingEyePosition(ctx.player()) : ctx.playerHead(), new Vec3(faceX, faceY, faceZ), ctx.playerRotations()); Rotation actual = baritone.getLookBehavior().getAimProcessor().peekRotation(place); HitResult res = RayTraceUtils.rayTraceTowards(ctx.player(), actual, ctx.playerController().getBlockReachDistance(), wouldSneak); if (res != null && res.getType() == HitResult.Type.BLOCK && ((BlockHitResult) res).getBlockPos().equals(against1) && ((BlockHitResult) res).getBlockPos().relative(((BlockHitResult) res).getDirection()).equals(placeAt)) { state.setTarget(new MovementTarget(place, true)); found = true; if (!preferDown) { // if preferDown is true, we want the last option // if preferDown is false, we want the first break; } } } } if (ctx.getSelectedBlock().isPresent()) { BlockPos selectedBlock = ctx.getSelectedBlock().get(); Direction side = ((BlockHitResult) ctx.objectMouseOver()).getDirection(); // only way for selectedBlock.equals(placeAt) to be true is if it's replaceable if (selectedBlock.equals(placeAt) || (MovementHelper.canPlaceAgainst(ctx, selectedBlock) && selectedBlock.relative(side).equals(placeAt))) { if (wouldSneak) { state.setInput(Input.SNEAK, true); } ((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, placeAt.getX(), placeAt.getY(), placeAt.getZ()); return PlaceResult.READY_TO_PLACE; } } if (found) { if (wouldSneak) { state.setInput(Input.SNEAK, true); } ((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, placeAt.getX(), placeAt.getY(), placeAt.getZ()); return PlaceResult.ATTEMPTING; } return PlaceResult.NO_OPTION; } enum PlaceResult { READY_TO_PLACE, ATTEMPTING, NO_OPTION; } static boolean isTransparent(Block b) { return b instanceof AirBlock || b == Blocks.LAVA || b == Blocks.WATER; } static List<BetterBlockPos> steppingOnBlocks(IPlayerContext ctx) { List<BetterBlockPos> blocks = new ArrayList<>(); for (byte x = -1; x <= 1; x++) { for (byte z = -1; z <= 1; z++) { if (ctx.player().getBoundingBox().intersects(Vec3.atLowerCornerOf(ctx.player().blockPosition()).add(x, 0, z), Vec3.atLowerCornerOf(ctx.player().blockPosition()).add(x + 1, 1, z + 1))) { blocks.add(new BetterBlockPos(ctx.player().getBlockX() + x, ctx.player().getBlockY() - 1, ctx.player().getBlockZ() + z)); } } } return blocks; } } ================================================ FILE: src/main/java/baritone/pathing/movement/MovementOption.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement; import baritone.api.utils.input.Input; import net.minecraft.util.Mth; import java.util.stream.Stream; public record MovementOption(Input input1, Input input2, float motionX, float motionZ) { private static final float SPRINT_MULTIPLIER = 1.3f; public MovementOption(Input input1, float motionX, float motionZ) { this(input1, null, motionX, motionZ); } public void setInputs(MovementState movementState) { if (input1 != null) { movementState.setInput(input1, true); } if (input2 != null) { movementState.setInput(input2, true); } } public float distanceToSq(float otherX, float otherZ) { return Mth.abs(motionX() - otherX) + Mth.abs(motionZ() - otherZ); } public static Stream<MovementOption> getOptions(float motionX, float motionZ, boolean canSprint) { return Stream.of( new MovementOption(Input.MOVE_FORWARD, canSprint ? motionX * SPRINT_MULTIPLIER : motionX, canSprint ? motionZ * SPRINT_MULTIPLIER : motionZ), new MovementOption(Input.MOVE_BACK, -motionX, -motionZ), new MovementOption(Input.MOVE_LEFT, -motionZ, motionX), new MovementOption(Input.MOVE_RIGHT, motionZ, -motionX), new MovementOption(Input.MOVE_FORWARD, Input.MOVE_LEFT, (canSprint ? motionX * SPRINT_MULTIPLIER : motionX) - motionZ, (canSprint ? motionZ * SPRINT_MULTIPLIER : motionZ) + motionX), new MovementOption(Input.MOVE_FORWARD, Input.MOVE_RIGHT, (canSprint ? motionX * SPRINT_MULTIPLIER : motionX) + motionZ, (canSprint ? motionZ * SPRINT_MULTIPLIER : motionZ) - motionX), new MovementOption(Input.MOVE_BACK, Input.MOVE_LEFT, -motionX - motionZ, -motionZ + motionX), new MovementOption(Input.MOVE_BACK, Input.MOVE_RIGHT, -motionX + motionZ, -motionZ - motionX) ); } } ================================================ FILE: src/main/java/baritone/pathing/movement/MovementState.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.Rotation; import baritone.api.utils.input.Input; import java.util.HashMap; import java.util.Map; import java.util.Optional; public class MovementState { private MovementStatus status; private MovementTarget target = new MovementTarget(); private final Map<Input, Boolean> inputState = new HashMap<>(); public MovementState setStatus(MovementStatus status) { this.status = status; return this; } public MovementStatus getStatus() { return status; } public MovementTarget getTarget() { return this.target; } public MovementState setTarget(MovementTarget target) { this.target = target; return this; } public MovementState setInput(Input input, boolean forced) { this.inputState.put(input, forced); return this; } public Map<Input, Boolean> getInputStates() { return this.inputState; } public static class MovementTarget { /** * Yaw and pitch angles that must be matched */ public Rotation rotation; /** * Whether or not this target must force rotations. * <p> * {@code true} if we're trying to place or break blocks, {@code false} if we're trying to look at the movement location */ private boolean forceRotations; public MovementTarget() { this(null, false); } public MovementTarget(Rotation rotation, boolean forceRotations) { this.rotation = rotation; this.forceRotations = forceRotations; } public final Optional<Rotation> getRotation() { return Optional.ofNullable(this.rotation); } public boolean hasToForceRotations() { return this.forceRotations; } } } ================================================ FILE: src/main/java/baritone/pathing/movement/Moves.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement; import baritone.api.utils.BetterBlockPos; import baritone.pathing.movement.movements.*; import baritone.utils.pathing.MutableMoveResult; import net.minecraft.core.Direction; /** * An enum of all possible movements attached to all possible directions they could be taken in * * @author leijurv */ public enum Moves { DOWNWARD(0, -1, 0) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementDownward(context.getBaritone(), src, src.below()); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementDownward.cost(context, x, y, z); } }, PILLAR(0, +1, 0) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementPillar(context.getBaritone(), src, src.above()); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementPillar.cost(context, x, y, z); } }, TRAVERSE_NORTH(0, 0, -1) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(context.getBaritone(), src, src.north()); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementTraverse.cost(context, x, y, z, x, z - 1); } }, TRAVERSE_SOUTH(0, 0, +1) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(context.getBaritone(), src, src.south()); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementTraverse.cost(context, x, y, z, x, z + 1); } }, TRAVERSE_EAST(+1, 0, 0) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(context.getBaritone(), src, src.east()); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementTraverse.cost(context, x, y, z, x + 1, z); } }, TRAVERSE_WEST(-1, 0, 0) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(context.getBaritone(), src, src.west()); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementTraverse.cost(context, x, y, z, x - 1, z); } }, ASCEND_NORTH(0, +1, -1) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(context.getBaritone(), src, new BetterBlockPos(src.x, src.y + 1, src.z - 1)); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementAscend.cost(context, x, y, z, x, z - 1); } }, ASCEND_SOUTH(0, +1, +1) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(context.getBaritone(), src, new BetterBlockPos(src.x, src.y + 1, src.z + 1)); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementAscend.cost(context, x, y, z, x, z + 1); } }, ASCEND_EAST(+1, +1, 0) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(context.getBaritone(), src, new BetterBlockPos(src.x + 1, src.y + 1, src.z)); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementAscend.cost(context, x, y, z, x + 1, z); } }, ASCEND_WEST(-1, +1, 0) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(context.getBaritone(), src, new BetterBlockPos(src.x - 1, src.y + 1, src.z)); } @Override public double cost(CalculationContext context, int x, int y, int z) { return MovementAscend.cost(context, x, y, z, x - 1, z); } }, DESCEND_EAST(+1, -1, 0, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); if (res.y == src.y - 1) { return new MovementDescend(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } else { return new MovementFall(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDescend.cost(context, x, y, z, x + 1, z, result); } }, DESCEND_WEST(-1, -1, 0, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); if (res.y == src.y - 1) { return new MovementDescend(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } else { return new MovementFall(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDescend.cost(context, x, y, z, x - 1, z, result); } }, DESCEND_NORTH(0, -1, -1, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); if (res.y == src.y - 1) { return new MovementDescend(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } else { return new MovementFall(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDescend.cost(context, x, y, z, x, z - 1, result); } }, DESCEND_SOUTH(0, -1, +1, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); if (res.y == src.y - 1) { return new MovementDescend(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } else { return new MovementFall(context.getBaritone(), src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDescend.cost(context, x, y, z, x, z + 1, result); } }, DIAGONAL_NORTHEAST(+1, 0, -1, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); return new MovementDiagonal(context.getBaritone(), src, Direction.NORTH, Direction.EAST, res.y - src.y); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDiagonal.cost(context, x, y, z, x + 1, z - 1, result); } }, DIAGONAL_NORTHWEST(-1, 0, -1, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); return new MovementDiagonal(context.getBaritone(), src, Direction.NORTH, Direction.WEST, res.y - src.y); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDiagonal.cost(context, x, y, z, x - 1, z - 1, result); } }, DIAGONAL_SOUTHEAST(+1, 0, +1, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); return new MovementDiagonal(context.getBaritone(), src, Direction.SOUTH, Direction.EAST, res.y - src.y); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDiagonal.cost(context, x, y, z, x + 1, z + 1, result); } }, DIAGONAL_SOUTHWEST(-1, 0, +1, false, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { MutableMoveResult res = new MutableMoveResult(); apply(context, src.x, src.y, src.z, res); return new MovementDiagonal(context.getBaritone(), src, Direction.SOUTH, Direction.WEST, res.y - src.y); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementDiagonal.cost(context, x, y, z, x - 1, z + 1, result); } }, PARKOUR_NORTH(0, 0, -4, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, Direction.NORTH); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementParkour.cost(context, x, y, z, Direction.NORTH, result); } }, PARKOUR_SOUTH(0, 0, +4, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, Direction.SOUTH); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementParkour.cost(context, x, y, z, Direction.SOUTH, result); } }, PARKOUR_EAST(+4, 0, 0, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, Direction.EAST); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementParkour.cost(context, x, y, z, Direction.EAST, result); } }, PARKOUR_WEST(-4, 0, 0, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, Direction.WEST); } @Override public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { MovementParkour.cost(context, x, y, z, Direction.WEST, result); } }; public final boolean dynamicXZ; public final boolean dynamicY; public final int xOffset; public final int yOffset; public final int zOffset; Moves(int x, int y, int z, boolean dynamicXZ, boolean dynamicY) { this.xOffset = x; this.yOffset = y; this.zOffset = z; this.dynamicXZ = dynamicXZ; this.dynamicY = dynamicY; } Moves(int x, int y, int z) { this(x, y, z, false, false); } public abstract Movement apply0(CalculationContext context, BetterBlockPos src); public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { if (dynamicXZ || dynamicY) { throw new UnsupportedOperationException("Movements with dynamic offset must override `apply`"); } result.x = x + xOffset; result.y = y + yOffset; result.z = z + zOffset; result.cost = cost(context, x, y, z); } public double cost(CalculationContext context, int x, int y, int z) { throw new UnsupportedOperationException("Movements must override `cost` or `apply`"); } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementAscend.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import com.google.common.collect.ImmutableSet; import java.util.Set; import net.minecraft.core.Direction; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.FallingBlock; import net.minecraft.world.level.block.state.BlockState; public class MovementAscend extends Movement { private int ticksWithoutPlacement = 0; public MovementAscend(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest) { super(baritone, src, dest, new BetterBlockPos[]{dest, src.above(2), dest.above()}, dest.below()); } @Override public void reset() { super.reset(); ticksWithoutPlacement = 0; } @Override public double calculateCost(CalculationContext context) { return cost(context, src.x, src.y, src.z, dest.x, dest.z); } @Override protected Set<BetterBlockPos> calculateValidPositions() { BetterBlockPos prior = new BetterBlockPos(src.subtract(getDirection()).above()); // sometimes we back up to place the block, also sprint ascends, also skip descend to straight ascend return ImmutableSet.of(src, src.above(), dest, prior, prior.above() ); } public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { BlockState toPlace = context.get(destX, y, destZ); double additionalPlacementCost = 0; if (!MovementHelper.canWalkOn(context, destX, y, destZ, toPlace)) { additionalPlacementCost = context.costOfPlacingAt(destX, y, destZ, toPlace); if (additionalPlacementCost >= COST_INF) { return COST_INF; } if (!MovementHelper.isReplaceable(destX, y, destZ, toPlace, context.bsi)) { return COST_INF; } boolean foundPlaceOption = false; for (int i = 0; i < 5; i++) { int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getStepX(); int againstY = y + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getStepY(); int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getStepZ(); if (againstX == x && againstZ == z) { // we might be able to backplace now, but it doesn't matter because it will have been broken by the time we'd need to use it continue; } if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) { foundPlaceOption = true; break; } } if (!foundPlaceOption) { // didn't find a valid place =( return COST_INF; } } BlockState srcUp2 = context.get(x, y + 2, z); // used lower down anyway if (context.get(x, y + 3, z).getBlock() instanceof FallingBlock && (MovementHelper.canWalkThrough(context, x, y + 1, z) || !(srcUp2.getBlock() instanceof FallingBlock))) {//it would fall on us and possibly suffocate us // HOWEVER, we assume that we're standing in the start position // that means that src and src.up(1) are both air // maybe they aren't now, but they will be by the time this starts // if the lower one is can't walk through and the upper one is falling, that means that by standing on src // (the presupposition of this Movement) // we have necessarily already cleared the entire FallingBlock stack // on top of our head // as in, if we have a block, then two FallingBlocks on top of it // and that block is x, y+1, z, and we'd have to clear it to even start this movement // we don't need to worry about those FallingBlocks because we've already cleared them return COST_INF; // you may think we only need to check srcUp2, not srcUp // however, in the scenario where glitchy world gen where unsupported sand / gravel generates // it's possible srcUp is AIR from the start, and srcUp2 is falling // and in that scenario, when we arrive and break srcUp2, that lets srcUp3 fall on us and suffocate us } BlockState srcDown = context.get(x, y - 1, z); if (srcDown.getBlock() == Blocks.LADDER || srcDown.getBlock() == Blocks.VINE) { return COST_INF; } // we can jump from soul sand, but not from a bottom slab boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown); boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace); if (jumpingFromBottomSlab && !jumpingToBottomSlab) { return COST_INF;// the only thing we can ascend onto from a bottom slab is another bottom slab } double walk; if (jumpingToBottomSlab) { if (jumpingFromBottomSlab) { walk = Math.max(JUMP_ONE_BLOCK_COST, WALK_ONE_BLOCK_COST); // we hit space immediately on entering this action walk += context.jumpPenalty; } else { walk = WALK_ONE_BLOCK_COST; // we don't hit space we just walk into the slab } } else { // jumpingFromBottomSlab must be false if (toPlace.is(Blocks.SOUL_SAND)) { walk = WALK_ONE_OVER_SOUL_SAND_COST; } else if (toPlace.is(Blocks.MAGMA_BLOCK)) { walk = SNEAK_ONE_BLOCK_COST; } else { walk = Math.max(JUMP_ONE_BLOCK_COST, WALK_ONE_BLOCK_COST); } walk += context.jumpPenalty; } double totalCost = walk + additionalPlacementCost; // start with srcUp2 since we already have its state // includeFalling isn't needed because of the falling check above -- if srcUp3 is falling we will have already exited with COST_INF if we'd actually have to break it totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false); if (totalCost >= COST_INF) { return COST_INF; } totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, false); if (totalCost >= COST_INF) { return COST_INF; } totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 2, destZ, true); return totalCost; } @Override public MovementState updateState(MovementState state) { if (ctx.playerFeet().y < src.y) { // this check should run even when in preparing state (breaking blocks) return state.setStatus(MovementStatus.UNREACHABLE); } super.updateState(state); // TODO incorporate some behavior from ActionClimb (specifically how it waited until it was at most 1.2 blocks away before starting to jump // for efficiency in ascending minimal height staircases, which is just repeated MovementAscend, so that it doesn't bonk its head on the ceiling repeatedly) if (state.getStatus() != MovementStatus.RUNNING) { return state; } if (ctx.playerFeet().equals(dest) || ctx.playerFeet().equals(dest.offset(getDirection().below()))) { return state.setStatus(MovementStatus.SUCCESS); } BlockState jumpingOnto = BlockStateInterface.get(ctx, positionToPlace); if (!MovementHelper.canWalkOn(ctx, positionToPlace, jumpingOnto)) { ticksWithoutPlacement++; if (MovementHelper.attemptToPlaceABlock(state, baritone, dest.below(), false, true) == PlaceResult.READY_TO_PLACE) { state.setInput(Input.SNEAK, true); if (ctx.player().isCrouching()) { state.setInput(Input.CLICK_RIGHT, true); } } if (ticksWithoutPlacement > 10) { // After 10 ticks without placement, we might be standing in the way, move back state.setInput(Input.MOVE_BACK, true); } return state; } MovementHelper.moveTowards(ctx, state, dest); state.setInput(Input.SNEAK, Baritone.settings().allowWalkOnMagmaBlocks.value && jumpingOnto.is(Blocks.MAGMA_BLOCK)); if (MovementHelper.isBottomSlab(jumpingOnto) && !MovementHelper.isBottomSlab(BlockStateInterface.get(ctx, src.below()))) { return state; // don't jump while walking from a non double slab into a bottom slab } if (Baritone.settings().assumeStep.value || ctx.playerFeet().equals(src.above())) { // no need to hit space if we're already jumping return state; } int xAxis = Math.abs(src.getX() - dest.getX()); // either 0 or 1 int zAxis = Math.abs(src.getZ() - dest.getZ()); // either 0 or 1 double flatDistToNext = xAxis * Math.abs((dest.getX() + 0.5D) - ctx.player().position().x) + zAxis * Math.abs((dest.getZ() + 0.5D) - ctx.player().position().z); double sideDist = zAxis * Math.abs((dest.getX() + 0.5D) - ctx.player().position().x) + xAxis * Math.abs((dest.getZ() + 0.5D) - ctx.player().position().z); double lateralMotion = xAxis * ctx.player().getDeltaMovement().z + zAxis * ctx.player().getDeltaMovement().x; if (Math.abs(lateralMotion) > 0.1) { return state; } if (headBonkClear()) { return state.setInput(Input.JUMP, true); } if (flatDistToNext > 1.2 || sideDist > 0.2) { return state; } // Once we are pointing the right way and moving, start jumping // This is slightly more efficient because otherwise we might start jumping before moving, and fall down without moving onto the block we want to jump onto // Also wait until we are close enough, because we might jump and hit our head on an adjacent block return state.setInput(Input.JUMP, true); } public boolean headBonkClear() { BetterBlockPos startUp = src.above(2); for (int i = 0; i < 4; i++) { BetterBlockPos check = startUp.relative(Direction.from2DDataValue(i)); if (!MovementHelper.canWalkThrough(ctx, check)) { // We might bonk our head return false; } } return true; } @Override public boolean safeToCancel(MovementState state) { // if we had to place, don't allow pause return state.getStatus() != MovementStatus.RUNNING || ticksWithoutPlacement == 0; } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementDescend.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; import com.google.common.collect.ImmutableSet; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.FallingBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import java.util.Set; public class MovementDescend extends Movement { private int numTicks = 0; public boolean forceSafeMode = false; public MovementDescend(IBaritone baritone, BetterBlockPos start, BetterBlockPos end) { super(baritone, start, end, new BetterBlockPos[]{end.above(2), end.above(), end}, end.below()); } @Override public void reset() { super.reset(); numTicks = 0; forceSafeMode = false; } /** * Called by PathExecutor if needing safeMode can only be detected with knowledge about the next movement */ public void forceSafeMode() { forceSafeMode = true; } @Override public double calculateCost(CalculationContext context) { MutableMoveResult result = new MutableMoveResult(); cost(context, src.x, src.y, src.z, dest.x, dest.z, result); if (result.y != dest.y) { return COST_INF; // doesn't apply to us, this position is a fall not a descend } return result.cost; } @Override protected Set<BetterBlockPos> calculateValidPositions() { return ImmutableSet.of(src, dest.above(), dest); } public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { double totalCost = 0; BlockState destDown = context.get(destX, y - 1, destZ); totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, destDown, false); if (totalCost >= COST_INF) { return; } totalCost += MovementHelper.getMiningDurationTicks(context, destX, y, destZ, false); if (totalCost >= COST_INF) { return; } totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, true); // only the top block in the 3 we need to mine needs to consider the falling blocks above if (totalCost >= COST_INF) { return; } Block fromDown = context.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { return; } // A //SA // A // B // C // D //if S is where you start, B needs to be air for a movementfall //A is plausibly breakable by either descend or fall //C, D, etc determine the length of the fall BlockState below = context.get(destX, y - 2, destZ); if (!MovementHelper.canWalkOn(context, destX, y - 2, destZ, below)) { dynamicFallCost(context, x, y, z, destX, destZ, totalCost, below, res); return; } if (destDown.getBlock() == Blocks.LADDER || destDown.getBlock() == Blocks.VINE) { return; } if (MovementHelper.canUseFrostWalker(context, destDown)) { // no need to check assumeWalkOnWater return; // the water will freeze when we try to walk into it } // we walk half the block plus 0.3 to get to the edge, then we walk the other 0.2 while simultaneously falling (math.max because of how it's in parallel) double walk = WALK_OFF_BLOCK_COST; if (fromDown == Blocks.SOUL_SAND) { // use this ratio to apply the soul sand speed penalty to our 0.8 block distance walk *= WALK_ONE_OVER_SOUL_SAND_COST / WALK_ONE_BLOCK_COST; } totalCost += walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); res.x = destX; res.y = y - 1; res.z = destZ; res.cost = totalCost; } public static boolean dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak, BlockState below, MutableMoveResult res) { if (frontBreak != 0 && context.get(destX, y + 2, destZ).getBlock() instanceof FallingBlock) { // if frontBreak is 0 we can actually get through this without updating the falling block and making it actually fall // but if frontBreak is nonzero, we're breaking blocks in front, so don't let anything fall through this column, // and potentially replace the water we're going to fall into return false; } if (!MovementHelper.canWalkThrough(context, destX, y - 2, destZ, below)) { return false; } double costSoFar = 0; int effectiveStartHeight = y; for (int fallHeight = 3; true; fallHeight++) { int newY = y - fallHeight; if (newY < context.world.getMinBuildHeight()) { // when pathing in the end, where you could plausibly fall into the void // this check prevents it from getting the block at y=(below whatever the minimum height is) and crashing return false; } boolean reachedMinimum = fallHeight >= context.minFallHeight; BlockState ontoBlock = context.get(destX, newY, destZ); int unprotectedFallHeight = fallHeight - (y - effectiveStartHeight); // equal to fallHeight - y + effectiveFallHeight, which is equal to -newY + effectiveFallHeight, which is equal to effectiveFallHeight - newY double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[unprotectedFallHeight] + frontBreak + costSoFar; if (reachedMinimum && MovementHelper.isWater(ontoBlock)) { if (!MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) { return false; } if (context.assumeWalkOnWater) { return false; // TODO fix } if (MovementHelper.isFlowing(destX, newY, destZ, ontoBlock, context.bsi)) { return false; // TODO flowing check required here? } if (!MovementHelper.canWalkOn(context, destX, newY - 1, destZ)) { // we could punch right through the water into something else return false; } // found a fall into water res.x = destX; res.y = newY; res.z = destZ; res.cost = tentativeCost;// TODO incorporate water swim up cost? return false; } if (reachedMinimum && context.allowFallIntoLava && MovementHelper.isLava(ontoBlock)) { // found a fall into lava res.x = destX; res.y = newY; res.z = destZ; res.cost = tentativeCost; return false; } if (unprotectedFallHeight <= 11 && (ontoBlock.getBlock() == Blocks.VINE || ontoBlock.getBlock() == Blocks.LADDER)) { // if fall height is greater than or equal to 11, we don't actually grab on to vines or ladders. the more you know // this effectively "resets" our falling speed costSoFar += FALL_N_BLOCKS_COST[unprotectedFallHeight - 1];// we fall until the top of this block (not including this block) costSoFar += LADDER_DOWN_ONE_COST; effectiveStartHeight = newY; continue; } if (MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) { continue; } if (!MovementHelper.canWalkOn(context, destX, newY, destZ, ontoBlock)) { return false; } if (MovementHelper.isBottomSlab(ontoBlock)) { return false; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect } if (reachedMinimum && unprotectedFallHeight <= context.maxFallHeightNoWater + 1) { // fallHeight = 4 means onto.up() is 3 blocks down, which is the max res.x = destX; res.y = newY + 1; res.z = destZ; res.cost = tentativeCost; return false; } if (reachedMinimum && context.hasWaterBucket && unprotectedFallHeight <= context.maxFallHeightBucket + 1) { res.x = destX; res.y = newY + 1;// this is the block we're falling onto, so dest is +1 res.z = destZ; res.cost = tentativeCost + context.placeBucketCost(); return true; } else { return false; } } } @Override public MovementState updateState(MovementState state) { super.updateState(state); if (state.getStatus() != MovementStatus.RUNNING) { return state; } BlockPos playerFeet = ctx.playerFeet(); BlockPos fakeDest = new BlockPos(dest.getX() * 2 - src.getX(), dest.getY(), dest.getZ() * 2 - src.getZ()); if ((playerFeet.equals(dest) || playerFeet.equals(fakeDest)) && (MovementHelper.isLiquid(ctx, dest) || ctx.player().position().y - dest.getY() < 0.5)) { // lilypads // Wait until we're actually on the ground before saying we're done because sometimes we continue to fall if the next action starts immediately return state.setStatus(MovementStatus.SUCCESS); /* else { // System.out.println(player().position().y + " " + playerFeet.getY() + " " + (player().position().y - playerFeet.getY())); }*/ } if (safeMode()) { double destX = (src.getX() + 0.5) * 0.17 + (dest.getX() + 0.5) * 0.83; double destZ = (src.getZ() + 0.5) * 0.17 + (dest.getZ() + 0.5) * 0.83; state.setTarget(new MovementState.MovementTarget( RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3(destX, dest.getY(), destZ), ctx.playerRotations()).withPitch(ctx.playerRotations().getPitch()), false )).setInput(Input.MOVE_FORWARD, true); return state; } double diffX = ctx.player().position().x - (dest.getX() + 0.5); double diffZ = ctx.player().position().z - (dest.getZ() + 0.5); double ab = Math.sqrt(diffX * diffX + diffZ * diffZ); double x = ctx.player().position().x - (src.getX() + 0.5); double z = ctx.player().position().z - (src.getZ() + 0.5); double fromStart = Math.sqrt(x * x + z * z); state.setInput(Input.SNEAK, Baritone.settings().allowWalkOnMagmaBlocks.value && ctx.world().getBlockState(ctx.player().blockPosition().below()).is(Blocks.MAGMA_BLOCK)); if (!playerFeet.equals(dest) || ab > 0.25) { if (numTicks++ < 20 && fromStart < 1.25) { MovementHelper.moveTowards(ctx, state, fakeDest); } else { MovementHelper.moveTowards(ctx, state, dest); } } return state; } public boolean safeMode() { if (forceSafeMode) { return true; } // (dest - src) + dest is offset 1 more in the same direction // so it's the block we'd need to worry about running into if we decide to sprint straight through this descend BlockPos into = dest.subtract(src.below()).offset(dest); if (skipToAscend()) { // if dest extends into can't walk through, but the two above are can walk through, then we can overshoot and glitch in that weird way return true; } for (int y = 0; y <= 2; y++) { // we could hit any of the three blocks if (MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, into.above(y)))) { return true; } } return false; } public boolean skipToAscend() { BlockPos into = dest.subtract(src.below()).offset(dest); return !MovementHelper.canWalkThrough(ctx, new BetterBlockPos(into)) && MovementHelper.canWalkThrough(ctx, new BetterBlockPos(into).above()) && MovementHelper.canWalkThrough(ctx, new BetterBlockPos(into).above(2)); } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; import com.google.common.collect.ImmutableSet; import java.util.ArrayList; import java.util.List; import java.util.Set; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; public class MovementDiagonal extends Movement { private static final double SQRT_2 = Math.sqrt(2); public MovementDiagonal(IBaritone baritone, BetterBlockPos start, Direction dir1, Direction dir2, int dy) { this(baritone, start, start.relative(dir1), start.relative(dir2), dir2, dy); // super(start, start.offset(dir1).offset(dir2), new BlockPos[]{start.offset(dir1), start.offset(dir1).up(), start.offset(dir2), start.offset(dir2).up(), start.offset(dir1).offset(dir2), start.offset(dir1).offset(dir2).up()}, new BlockPos[]{start.offset(dir1).offset(dir2).down()}); } private MovementDiagonal(IBaritone baritone, BetterBlockPos start, BetterBlockPos dir1, BetterBlockPos dir2, Direction drr2, int dy) { this(baritone, start, dir1.relative(drr2).above(dy), dir1, dir2); } private MovementDiagonal(IBaritone baritone, BetterBlockPos start, BetterBlockPos end, BetterBlockPos dir1, BetterBlockPos dir2) { super(baritone, start, end, new BetterBlockPos[]{dir1, dir1.above(), dir2, dir2.above(), end, end.above()}); } @Override protected boolean safeToCancel(MovementState state) { //too simple. backfill does not work after cornering with this //return context.precomputedData.canWalkOn(ctx, ctx.playerFeet().down()); LocalPlayer player = ctx.player(); double offset = 0.25; double x = player.position().x; double y = player.position().y - 1; double z = player.position().z; //standard if (ctx.playerFeet().equals(src)) { return true; } //both corners are walkable if (MovementHelper.canWalkOn(ctx, new BlockPos(src.x, src.y - 1, dest.z)) && MovementHelper.canWalkOn(ctx, new BlockPos(dest.x, src.y - 1, src.z))) { return true; } //we are in a likely unwalkable corner, check for a supporting block if (ctx.playerFeet().equals(new BetterBlockPos(src.x, src.y, dest.z)) || ctx.playerFeet().equals(new BetterBlockPos(dest.x, src.y, src.z))) { return (MovementHelper.canWalkOn(ctx, new BetterBlockPos(x + offset, y, z + offset)) || MovementHelper.canWalkOn(ctx, new BetterBlockPos(x + offset, y, z - offset)) || MovementHelper.canWalkOn(ctx, new BetterBlockPos(x - offset, y, z + offset)) || MovementHelper.canWalkOn(ctx, new BetterBlockPos(x - offset, y, z - offset))); } return true; } @Override public double calculateCost(CalculationContext context) { MutableMoveResult result = new MutableMoveResult(); cost(context, src.x, src.y, src.z, dest.x, dest.z, result); if (result.y != dest.y) { return COST_INF; // doesn't apply to us, this position is incorrect } return result.cost; } @Override protected Set<BetterBlockPos> calculateValidPositions() { BetterBlockPos diagA = new BetterBlockPos(src.x, src.y, dest.z); BetterBlockPos diagB = new BetterBlockPos(dest.x, src.y, src.z); if (dest.y < src.y) { return ImmutableSet.of(src, dest.above(), diagA, diagB, dest, diagA.below(), diagB.below()); } if (dest.y > src.y) { return ImmutableSet.of(src, src.above(), diagA, diagB, dest, diagA.above(), diagB.above()); } return ImmutableSet.of(src, dest, diagA, diagB); } public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { if (!MovementHelper.canWalkThrough(context, destX, y + 1, destZ)) { return; } BlockState destInto = context.get(destX, y, destZ); BlockState fromDown; boolean ascend = false; BlockState destWalkOn; boolean descend = false; boolean frostWalker = false; boolean sneaking = false; if (!MovementHelper.canWalkThrough(context, destX, y, destZ, destInto)) { ascend = true; if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context, x, y + 2, z) || !MovementHelper.canWalkOn(context, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context, destX, y + 2, destZ)) { return; } destWalkOn = destInto; fromDown = context.get(x, y - 1, z); } else { destWalkOn = context.get(destX, y - 1, destZ); fromDown = context.get(x, y - 1, z); boolean standingOnABlock = MovementHelper.mustBeSolidToWalkOn(context, x, y - 1, z, fromDown); frostWalker = standingOnABlock && MovementHelper.canUseFrostWalker(context, destWalkOn); if (!frostWalker && !MovementHelper.canWalkOn(context, destX, y - 1, destZ, destWalkOn)) { descend = true; if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context, destX, y - 1, destZ, destWalkOn)) { return; } } frostWalker &= !context.assumeWalkOnWater; // do this after checking for descends because jesus can't prevent the water from freezing, it just prevents us from relying on the water freezing } double multiplier = WALK_ONE_BLOCK_COST; // For either possible soul sand, that affects half of our walking if (destWalkOn.is(Blocks.SOUL_SAND)) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } else if (context.allowWalkOnMagmaBlocks && destWalkOn.is(Blocks.MAGMA_BLOCK)) { multiplier += (SNEAK_ONE_BLOCK_COST - WALK_ONE_BLOCK_COST) / 2; sneaking = true; } else if (frostWalker) { // frostwalker lets us walk on water without the penalty } else if (destWalkOn.getBlock() == Blocks.WATER) { multiplier += context.walkOnWaterOnePenalty * SQRT_2; } Block fromDownBlock = fromDown.getBlock(); if (fromDownBlock == Blocks.LADDER || fromDownBlock == Blocks.VINE) { return; } if (fromDownBlock == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } else if (context.allowWalkOnMagmaBlocks && fromDownBlock.equals(Blocks.MAGMA_BLOCK)) { multiplier += (SNEAK_ONE_BLOCK_COST - WALK_ONE_BLOCK_COST) / 2; sneaking = true; } BlockState cuttingOver1 = context.get(x, y - 1, destZ); if ((!context.allowWalkOnMagmaBlocks && cuttingOver1.is(Blocks.MAGMA_BLOCK)) || MovementHelper.isLava(cuttingOver1)) { return; } BlockState cuttingOver2 = context.get(destX, y - 1, z); if ((!context.allowWalkOnMagmaBlocks && cuttingOver1.is(Blocks.MAGMA_BLOCK)) || MovementHelper.isLava(cuttingOver2)) { return; } boolean water = false; BlockState startState = context.get(x, y, z); Block startIn = startState.getBlock(); if (MovementHelper.isWater(startState) || MovementHelper.isWater(destInto)) { if (ascend) { return; } // Ignore previous multiplier // Whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water // Not even touching the blocks below multiplier = context.waterWalkSpeed; water = true; } BlockState pb0 = context.get(x, y, destZ); BlockState pb2 = context.get(destX, y, z); if (ascend) { boolean ATop = MovementHelper.canWalkThrough(context, x, y + 2, destZ); boolean AMid = MovementHelper.canWalkThrough(context, x, y + 1, destZ); boolean ALow = MovementHelper.canWalkThrough(context, x, y, destZ, pb0); boolean BTop = MovementHelper.canWalkThrough(context, destX, y + 2, z); boolean BMid = MovementHelper.canWalkThrough(context, destX, y + 1, z); boolean BLow = MovementHelper.canWalkThrough(context, destX, y, z, pb2); if ((!(ATop && AMid && ALow) && !(BTop && BMid && BLow)) // no option || MovementHelper.avoidWalkingInto(pb0) // bad || MovementHelper.avoidWalkingInto(pb2) // bad || (ATop && AMid && MovementHelper.canWalkOn(context, x, y, destZ, pb0)) // we could just ascend || (BTop && BMid && MovementHelper.canWalkOn(context, destX, y, z, pb2)) // we could just ascend || (!ATop && AMid && ALow) // head bonk A || (!BTop && BMid && BLow)) { // head bonk B return; } res.cost = multiplier * SQRT_2 + JUMP_ONE_BLOCK_COST; res.x = destX; res.z = destZ; res.y = y + 1; return; } double optionA = MovementHelper.getMiningDurationTicks(context, x, y, destZ, pb0, false); double optionB = MovementHelper.getMiningDurationTicks(context, destX, y, z, pb2, false); if (optionA != 0 && optionB != 0) { // check these one at a time -- if pb0 and pb2 were nonzero, we already know that (optionA != 0 && optionB != 0) // so no need to check pb1 as well, might as well return early here return; } BlockState pb1 = context.get(x, y + 1, destZ); optionA += MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true); if (optionA != 0 && optionB != 0) { // same deal, if pb1 makes optionA nonzero and option B already was nonzero, pb3 can't affect the result return; } BlockState pb3 = context.get(destX, y + 1, z); if (optionA == 0 && ((MovementHelper.avoidWalkingInto(pb2) && pb2.getBlock() != Blocks.WATER) || MovementHelper.avoidWalkingInto(pb3))) { // at this point we're done calculating optionA, so we can check if it's actually possible to edge around in that direction return; } optionB += MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true); if (optionA != 0 && optionB != 0) { // and finally, if the cost is nonzero for both ways to approach this diagonal, it's not possible return; } if (optionB == 0 && ((MovementHelper.avoidWalkingInto(pb0) && pb0.getBlock() != Blocks.WATER) || MovementHelper.avoidWalkingInto(pb1))) { // and now that option B is fully calculated, see if we can edge around that way return; } if (optionA != 0 || optionB != 0) { multiplier *= SQRT_2 - 0.001; // TODO tune if (startIn == Blocks.LADDER || startIn == Blocks.VINE) { // edging around doesn't work if doing so would climb a ladder or vine instead of moving sideways return; } } else { // only can sprint if not edging around if (context.canSprint && !water && !sneaking) { // If we aren't edging around anything, and we aren't in water // We can sprint =D // Don't check for soul sand, since we can sprint on that too multiplier *= SPRINT_MULTIPLIER; } } res.cost = multiplier * SQRT_2; if (descend) { res.cost += Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); res.y = y - 1; } else { res.y = y; } res.x = destX; res.z = destZ; } @Override public MovementState updateState(MovementState state) { super.updateState(state); if (state.getStatus() != MovementStatus.RUNNING) { return state; } if (ctx.playerFeet().equals(dest)) { return state.setStatus(MovementStatus.SUCCESS); } else if (!playerInValidPosition() && !(MovementHelper.isLiquid(ctx, src) && getValidPositions().contains(ctx.playerFeet().above()))) { return state.setStatus(MovementStatus.UNREACHABLE); } if (dest.y > src.y && ctx.player().position().y < src.y + 0.1 && ctx.player().horizontalCollision) { state.setInput(Input.JUMP, true); } if (sprint()) { state.setInput(Input.SPRINT, true); } state.setInput(Input.SNEAK, Baritone.settings().allowWalkOnMagmaBlocks.value && MovementHelper.steppingOnBlocks(ctx).stream().anyMatch(block -> ctx.world().getBlockState(block).is(Blocks.MAGMA_BLOCK))); MovementHelper.moveTowards(ctx, state, dest); return state; } private boolean sprint() { if (MovementHelper.isLiquid(ctx, ctx.playerFeet()) && !Baritone.settings().sprintInWater.value) { return false; } for (int i = 0; i < 4; i++) { if (!MovementHelper.canWalkThrough(ctx, positionsToBreak[i])) { return false; } } return true; } @Override protected boolean prepared(MovementState state) { return true; } @Override public List<BlockPos> toBreak(BlockStateInterface bsi) { if (toBreakCached != null) { return toBreakCached; } List<BlockPos> result = new ArrayList<>(); for (int i = 4; i < 6; i++) { if (!MovementHelper.canWalkThrough(bsi, positionsToBreak[i].x, positionsToBreak[i].y, positionsToBreak[i].z)) { result.add(positionsToBreak[i]); } } toBreakCached = result; return result; } @Override public List<BlockPos> toWalkInto(BlockStateInterface bsi) { if (toWalkIntoCached == null) { toWalkIntoCached = new ArrayList<>(); } List<BlockPos> result = new ArrayList<>(); for (int i = 0; i < 4; i++) { if (!MovementHelper.canWalkThrough(bsi, positionsToBreak[i].x, positionsToBreak[i].y, positionsToBreak[i].z)) { result.add(positionsToBreak[i]); } } toWalkIntoCached = result; return toWalkIntoCached; } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementDownward.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import com.google.common.collect.ImmutableSet; import java.util.Set; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; public class MovementDownward extends Movement { private int numTicks = 0; public MovementDownward(IBaritone baritone, BetterBlockPos start, BetterBlockPos end) { super(baritone, start, end, new BetterBlockPos[]{end}); } @Override public void reset() { super.reset(); numTicks = 0; } @Override public double calculateCost(CalculationContext context) { return cost(context, src.x, src.y, src.z); } @Override protected Set<BetterBlockPos> calculateValidPositions() { return ImmutableSet.of(src, dest); } public static double cost(CalculationContext context, int x, int y, int z) { if (!context.allowDownward) { return COST_INF; } if (!MovementHelper.canWalkOn(context, x, y - 2, z)) { return COST_INF; } BlockState down = context.get(x, y - 1, z); Block downBlock = down.getBlock(); if (downBlock == Blocks.LADDER || downBlock == Blocks.VINE) { return LADDER_DOWN_ONE_COST; } else { // we're standing on it, while it might be block falling, it'll be air by the time we get here in the movement return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context, x, y - 1, z, down, false); } } @Override public MovementState updateState(MovementState state) { super.updateState(state); if (state.getStatus() != MovementStatus.RUNNING) { return state; } if (ctx.playerFeet().equals(dest)) { return state.setStatus(MovementStatus.SUCCESS); } else if (!playerInValidPosition()) { return state.setStatus(MovementStatus.UNREACHABLE); } double diffX = ctx.player().position().x - (dest.getX() + 0.5); double diffZ = ctx.player().position().z - (dest.getZ() + 0.5); double ab = Math.sqrt(diffX * diffX + diffZ * diffZ); if (numTicks++ < 10 && ab < 0.2) { return state; } MovementHelper.moveTowards(ctx, state, positionsToBreak[0]); return state; } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementFall.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.VecUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.pathing.movement.MovementState.MovementTarget; import baritone.utils.pathing.MutableMoveResult; import java.util.HashSet; import java.util.Optional; import java.util.Set; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LadderBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.WaterFluid; import net.minecraft.world.phys.Vec3; public class MovementFall extends Movement { private static final ItemStack STACK_BUCKET_WATER = new ItemStack(Items.WATER_BUCKET); private static final ItemStack STACK_BUCKET_EMPTY = new ItemStack(Items.BUCKET); public MovementFall(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest) { super(baritone, src, dest, MovementFall.buildPositionsToBreak(src, dest)); } @Override public double calculateCost(CalculationContext context) { MutableMoveResult result = new MutableMoveResult(); MovementDescend.cost(context, src.x, src.y, src.z, dest.x, dest.z, result); if (result.y != dest.y) { return COST_INF; // doesn't apply to us, this position is a descend not a fall } return result.cost; } @Override protected Set<BetterBlockPos> calculateValidPositions() { Set<BetterBlockPos> set = new HashSet<>(); set.add(src); for (int y = src.y - dest.y; y >= 0; y--) { set.add(dest.above(y)); } return set; } private boolean willPlaceBucket() { CalculationContext context = new CalculationContext(baritone); MutableMoveResult result = new MutableMoveResult(); return MovementDescend.dynamicFallCost(context, src.x, src.y, src.z, dest.x, dest.z, 0, context.get(dest.x, src.y - 2, dest.z), result); } @Override public MovementState updateState(MovementState state) { super.updateState(state); if (state.getStatus() != MovementStatus.RUNNING) { return state; } BlockPos playerFeet = ctx.playerFeet(); Rotation toDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations()); Rotation targetRotation = null; BlockState destState = ctx.world().getBlockState(dest); Block destBlock = destState.getBlock(); if (ctx.world().getBlockState(dest.below()).is(Blocks.MAGMA_BLOCK) && MovementHelper.steppingOnBlocks(ctx).stream().allMatch(block -> MovementHelper.canWalkThrough(ctx, block))) { state.setInput(Input.SNEAK, true); } boolean isWater = destState.getFluidState().getType() instanceof WaterFluid; if (!isWater && willPlaceBucket() && !playerFeet.equals(dest)) { if (!Inventory.isHotbarSlot(ctx.player().getInventory().findSlotMatchingItem(STACK_BUCKET_WATER)) || ctx.world().dimension() == Level.NETHER) { return state.setStatus(MovementStatus.UNREACHABLE); } if (ctx.player().position().y - dest.getY() < ctx.playerController().getBlockReachDistance() && !ctx.player().isOnGround()) { ctx.player().getInventory().selected = ctx.player().getInventory().findSlotMatchingItem(STACK_BUCKET_WATER); targetRotation = new Rotation(toDest.getYaw(), 90.0F); if (ctx.isLookingAt(dest) || ctx.isLookingAt(dest.below())) { state.setInput(Input.CLICK_RIGHT, true); } } } if (targetRotation != null) { state.setTarget(new MovementTarget(targetRotation, true)); } else { state.setTarget(new MovementTarget(toDest, false)); } if (playerFeet.equals(dest) && (ctx.player().position().y - playerFeet.getY() < 0.094 || isWater)) { // 0.094 because lilypads if (isWater) { // only match water, not flowing water (which we cannot pick up with a bucket) if (Inventory.isHotbarSlot(ctx.player().getInventory().findSlotMatchingItem(STACK_BUCKET_EMPTY))) { ctx.player().getInventory().selected = ctx.player().getInventory().findSlotMatchingItem(STACK_BUCKET_EMPTY); if (ctx.player().getDeltaMovement().y >= 0) { return state.setInput(Input.CLICK_RIGHT, true); } else { return state; } } else { if (ctx.player().getDeltaMovement().y >= 0) { return state.setStatus(MovementStatus.SUCCESS); } // don't else return state; we need to stay centered because this water might be flowing under the surface } } else { return state.setStatus(MovementStatus.SUCCESS); } } Vec3 destCenter = VecUtils.getBlockPosCenter(dest); // we are moving to the 0.5 center not the edge (like if we were falling on a ladder) if (Math.abs(ctx.player().position().x + ctx.player().getDeltaMovement().x - destCenter.x) > 0.1 || Math.abs(ctx.player().position().z + ctx.player().getDeltaMovement().z - destCenter.z) > 0.1) { if (!ctx.player().isOnGround() && Math.abs(ctx.player().getDeltaMovement().y) > 0.4) { state.setInput(Input.SNEAK, true); } state.setInput(Input.MOVE_FORWARD, true); } Vec3i avoid = Optional.ofNullable(avoid()).map(Direction::getNormal).orElse(null); if (avoid == null) { avoid = src.subtract(dest); } else { double dist = Math.abs(avoid.getX() * (destCenter.x - avoid.getX() / 2.0 - ctx.player().position().x)) + Math.abs(avoid.getZ() * (destCenter.z - avoid.getZ() / 2.0 - ctx.player().position().z)); if (dist < 0.6) { state.setInput(Input.MOVE_FORWARD, true); } else if (!ctx.player().isOnGround()) { state.setInput(Input.SNEAK, false); } } if (targetRotation == null) { Vec3 destCenterOffset = new Vec3(destCenter.x + 0.125 * avoid.getX(), destCenter.y, destCenter.z + 0.125 * avoid.getZ()); state.setTarget(new MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), destCenterOffset, ctx.playerRotations()), false)); } return state; } private Direction avoid() { for (int i = 0; i < 15; i++) { BlockState state = ctx.world().getBlockState(ctx.playerFeet().below(i)); if (state.getBlock() == Blocks.LADDER) { return state.getValue(LadderBlock.FACING); } } return null; } @Override public boolean safeToCancel(MovementState state) { // if we haven't started walking off the edge yet, or if we're in the process of breaking blocks before doing the fall // then it's safe to cancel this return ctx.playerFeet().equals(src) || state.getStatus() != MovementStatus.RUNNING; } private static BetterBlockPos[] buildPositionsToBreak(BetterBlockPos src, BetterBlockPos dest) { BetterBlockPos[] toBreak; int diffX = src.getX() - dest.getX(); int diffZ = src.getZ() - dest.getZ(); int diffY = Math.abs(src.getY() - dest.getY()); toBreak = new BetterBlockPos[diffY + 2]; for (int i = 0; i < toBreak.length; i++) { toBreak[i] = new BetterBlockPos(src.getX() - diffX, src.getY() + 1 - i, src.getZ() - diffZ); } return toBreak; } @Override protected boolean prepared(MovementState state) { if (state.getStatus() == MovementStatus.WAITING) { return true; } // only break if one of the first three needs to be broken // specifically ignore the last one which might be water for (int i = 0; i < 4 && i < positionsToBreak.length; i++) { if (!MovementHelper.canWalkThrough(ctx, positionsToBreak[i])) { return super.prepared(state); } } return true; } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementParkour.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; import net.minecraft.core.Direction; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.StairBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.WaterFluid; import java.util.HashSet; import java.util.Set; public class MovementParkour extends Movement { private static final BetterBlockPos[] EMPTY = new BetterBlockPos[]{}; private final Direction direction; private final int dist; private final boolean ascend; private MovementParkour(IBaritone baritone, BetterBlockPos src, int dist, Direction dir, boolean ascend) { super(baritone, src, src.relative(dir, dist).above(ascend ? 1 : 0), EMPTY, src.relative(dir, dist).below(ascend ? 0 : 1)); this.direction = dir; this.dist = dist; this.ascend = ascend; } public static MovementParkour cost(CalculationContext context, BetterBlockPos src, Direction direction) { MutableMoveResult res = new MutableMoveResult(); cost(context, src.x, src.y, src.z, direction, res); int dist = Math.abs(res.x - src.x) + Math.abs(res.z - src.z); return new MovementParkour(context.getBaritone(), src, dist, direction, res.y > src.y); } public static void cost(CalculationContext context, int x, int y, int z, Direction dir, MutableMoveResult res) { if (!context.allowParkour) { return; } if (!context.allowJumpAtBuildLimit && y >= context.world.getMaxBuildHeight()) { return; } int xDiff = dir.getStepX(); int zDiff = dir.getStepZ(); if (!MovementHelper.fullyPassable(context, x + xDiff, y, z + zDiff)) { // most common case at the top -- the adjacent block isn't air return; } BlockState adj = context.get(x + xDiff, y - 1, z + zDiff); if (MovementHelper.canWalkOn(context, x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now) // second most common case -- we could just traverse not parkour return; } if (MovementHelper.avoidWalkingInto(adj) && !(adj.getFluidState().getType() instanceof WaterFluid)) { // magma sucks return; } if (!MovementHelper.fullyPassable(context, x + xDiff, y + 1, z + zDiff)) { return; } if (!MovementHelper.fullyPassable(context, x + xDiff, y + 2, z + zDiff)) { return; } if (!MovementHelper.fullyPassable(context, x, y + 2, z)) { return; } BlockState standingOn = context.get(x, y - 1, z); if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || standingOn.getBlock() instanceof StairBlock || MovementHelper.isBottomSlab(standingOn)) { return; } // we can't jump from (frozen) water with assumeWalkOnWater because we can't be sure it will be frozen if (context.assumeWalkOnWater && !standingOn.getFluidState().isEmpty()) { return; } if (!context.get(x, y, z).getFluidState().isEmpty()) { return; // can't jump out of water } int maxJump; if (context.allowWalkOnMagmaBlocks && standingOn.is(Blocks.MAGMA_BLOCK)) { maxJump = 2; } else if (standingOn.getBlock() == Blocks.SOUL_SAND) { maxJump = 2; // 1 block gap } else if (context.canSprint) { maxJump = 4; } else { maxJump = 3; } // check parkour jumps from smallest to largest for obstacles/walls and landing positions int verifiedMaxJump = 1; // i - 1 (when i = 2) for (int i = 2; i <= maxJump; i++) { int destX = x + xDiff * i; int destZ = z + zDiff * i; // check head/feet if (!MovementHelper.fullyPassable(context, destX, y + 1, destZ)) { break; } if (!MovementHelper.fullyPassable(context, destX, y + 2, destZ)) { break; } // check for ascend landing position BlockState destInto = context.bsi.get0(destX, y, destZ); if (!MovementHelper.fullyPassable(context, destX, y, destZ, destInto)) { if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) { res.x = destX; res.y = y + 1; res.z = destZ; res.cost = i * SPRINT_ONE_BLOCK_COST + context.jumpPenalty; return; } break; } // check for flat landing position BlockState landingOn = context.bsi.get0(destX, y - 1, destZ); // farmland needs to be canWalkOn otherwise farm can never work at all, but we want to specifically disallow ending a jump on farmland haha // frostwalker works here because we can't jump from possibly unfrozen water if ((landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context, destX, y - 1, destZ, landingOn)) || (Math.min(16, context.frostWalker + 2) >= i && MovementHelper.canUseFrostWalker(context, landingOn)) ) { if (checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) { res.x = destX; res.y = y; res.z = destZ; res.cost = costFromJumpDistance(i) + context.jumpPenalty; return; } break; } if (!MovementHelper.fullyPassable(context, destX, y + 3, destZ)) { break; } verifiedMaxJump = i; } // parkour place starts here if (!context.allowParkourPlace) { return; } // check parkour jumps from largest to smallest for positions to place blocks for (int i = verifiedMaxJump; i > 1; i--) { int destX = x + i * xDiff; int destZ = z + i * zDiff; BlockState toReplace = context.get(destX, y - 1, destZ); double placeCost = context.costOfPlacingAt(destX, y - 1, destZ, toReplace); if (placeCost >= COST_INF) { continue; } if (!MovementHelper.isReplaceable(destX, y - 1, destZ, toReplace, context.bsi)) { continue; } if (!checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) { continue; } for (int j = 0; j < 5; j++) { int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[j].getStepX(); int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[j].getStepY(); int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[j].getStepZ(); if (againstX == destX - xDiff && againstZ == destZ - zDiff) { // we can't turn around that fast continue; } if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) { res.x = destX; res.y = y; res.z = destZ; res.cost = costFromJumpDistance(i) + placeCost + context.jumpPenalty; return; } } } } private static boolean checkOvershootSafety(BlockStateInterface bsi, int x, int y, int z) { // we're going to walk into these two blocks after the landing of the parkour anyway, so make sure they aren't avoidWalkingInto return !MovementHelper.avoidWalkingInto(bsi.get0(x, y, z)) && !MovementHelper.avoidWalkingInto(bsi.get0(x, y + 1, z)); } private static double costFromJumpDistance(int dist) { switch (dist) { case 2: return WALK_ONE_BLOCK_COST * 2; // IDK LOL case 3: return WALK_ONE_BLOCK_COST * 3; case 4: return SPRINT_ONE_BLOCK_COST * 4; default: throw new IllegalStateException("LOL " + dist); } } @Override public double calculateCost(CalculationContext context) { MutableMoveResult res = new MutableMoveResult(); cost(context, src.x, src.y, src.z, direction, res); if (res.x != dest.x || res.y != dest.y || res.z != dest.z) { return COST_INF; } return res.cost; } @Override protected Set<BetterBlockPos> calculateValidPositions() { Set<BetterBlockPos> set = new HashSet<>(); for (int i = 0; i <= dist; i++) { for (int y = 0; y < 2; y++) { set.add(src.relative(direction, i).above(y)); } } return set; } @Override public boolean safeToCancel(MovementState state) { // once this movement is instantiated, the state is default to PREPPING // but once it's ticked for the first time it changes to RUNNING // since we don't really know anything about momentum, it suffices to say Parkour can only be canceled on the 0th tick return state.getStatus() != MovementStatus.RUNNING; } @Override public MovementState updateState(MovementState state) { super.updateState(state); if (state.getStatus() != MovementStatus.RUNNING) { return state; } if (ctx.playerFeet().y < src.y) { // we have fallen logDebug("sorry"); return state.setStatus(MovementStatus.UNREACHABLE); } if (dist >= 4 || ascend) { state.setInput(Input.SPRINT, true); } if (Baritone.settings().allowWalkOnMagmaBlocks.value && ctx.world().getBlockState(ctx.playerFeet().below()).is(Blocks.MAGMA_BLOCK)) { state.setInput(Input.SNEAK, true); } MovementHelper.moveTowards(ctx, state, dest); if (ctx.playerFeet().equals(dest)) { Block d = BlockStateInterface.getBlock(ctx, dest); if (d == Blocks.VINE || d == Blocks.LADDER) { // it physically hurt me to add support for parkour jumping onto a vine // but i did it anyway return state.setStatus(MovementStatus.SUCCESS); } if (ctx.player().position().y - ctx.playerFeet().getY() < 0.094) { // lilypads state.setStatus(MovementStatus.SUCCESS); } } else if (!ctx.playerFeet().equals(src)) { if (ctx.playerFeet().equals(src.relative(direction)) || ctx.player().position().y - src.y > 0.0001) { if (Baritone.settings().allowPlace.value // see PR #3775 && ((Baritone) baritone).getInventoryBehavior().hasGenericThrowaway() && !MovementHelper.canWalkOn(ctx, dest.below()) && !ctx.player().isOnGround() && MovementHelper.attemptToPlaceABlock(state, baritone, dest.below(), true, false) == PlaceResult.READY_TO_PLACE ) { // go in the opposite order to check DOWN before all horizontals -- down is preferable because you don't have to look to the side while in midair, which could mess up the trajectory state.setInput(Input.CLICK_RIGHT, true); } // prevent jumping too late by checking for ascend if (dist == 3 && !ascend) { // this is a 2 block gap, dest = src + direction * 3 double xDiff = (src.x + 0.5) - ctx.player().position().x; double zDiff = (src.z + 0.5) - ctx.player().position().z; double distFromStart = Math.max(Math.abs(xDiff), Math.abs(zDiff)); if (distFromStart < 0.7) { return state; } } state.setInput(Input.JUMP, true); } else if (!ctx.playerFeet().equals(dest.relative(direction, -1))) { state.setInput(Input.SPRINT, false); if (ctx.playerFeet().equals(src.relative(direction, -1))) { MovementHelper.moveTowards(ctx, state, src); } else { MovementHelper.moveTowards(ctx, state, src.relative(direction, -1)); } } } return state; } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementPillar.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.VecUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import com.google.common.collect.ImmutableSet; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.CarpetBlock; import net.minecraft.world.level.block.FallingBlock; import net.minecraft.world.level.block.FenceGateBlock; import net.minecraft.world.level.block.LadderBlock; import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.SlabType; import net.minecraft.world.phys.Vec3; import java.util.Set; public class MovementPillar extends Movement { public MovementPillar(IBaritone baritone, BetterBlockPos start, BetterBlockPos end) { super(baritone, start, end, new BetterBlockPos[]{start.above(2)}, start); } @Override public double calculateCost(CalculationContext context) { return cost(context, src.x, src.y, src.z); } @Override protected Set<BetterBlockPos> calculateValidPositions() { return ImmutableSet.of(src, dest); } public static double cost(CalculationContext context, int x, int y, int z) { BlockState fromState = context.get(x, y, z); Block from = fromState.getBlock(); boolean ladder = from == Blocks.LADDER || from == Blocks.VINE; BlockState fromDown = context.get(x, y - 1, z); if (!ladder) { if (fromDown.getBlock() == Blocks.LADDER || fromDown.getBlock() == Blocks.VINE) { return COST_INF; // can't pillar from a ladder or vine onto something that isn't also climbable } if (fromDown.getBlock() instanceof SlabBlock && fromDown.getValue(SlabBlock.TYPE) == SlabType.BOTTOM) { return COST_INF; // can't pillar up from a bottom slab onto a non ladder } } if (from == Blocks.VINE && !hasAgainst(context, x, y, z)) { // TODO this vine can't be climbed, but we could place a pillar still since vines are replacable, no? perhaps the pillar jump would be impossible because of the slowdown actually. return COST_INF; } BlockState toBreak = context.get(x, y + 2, z); Block toBreakBlock = toBreak.getBlock(); if (toBreakBlock instanceof FenceGateBlock) { // see issue #172 return COST_INF; } BlockState srcUp = null; if (MovementHelper.isWater(toBreak) && MovementHelper.isWater(fromState)) { // TODO should this also be allowed if toBreakBlock is air? srcUp = context.get(x, y + 1, z); if (MovementHelper.isWater(srcUp)) { return LADDER_UP_ONE_COST; // allow ascending pillars of water, but only if we're already in one } } double placeCost = 0; if (!ladder) { // we need to place a block where we started to jump on it placeCost = context.costOfPlacingAt(x, y, z, fromState); if (placeCost >= COST_INF) { return COST_INF; } if (fromDown.getBlock() instanceof AirBlock) { placeCost += 0.1; // slightly (1/200th of a second) penalize pillaring on what's currently air } } if ((MovementHelper.isLiquid(fromState) && !MovementHelper.canPlaceAgainst(context.bsi, x, y - 1, z, fromDown)) || (MovementHelper.isLiquid(fromDown) && context.assumeWalkOnWater)) { // otherwise, if we're standing in water, we cannot pillar // if we're standing on water and assumeWalkOnWater is true, we cannot pillar // if we're standing on water and assumeWalkOnWater is false, we must have ascended to here, or sneak backplaced, so it is possible to pillar again return COST_INF; } if ((from == Blocks.LILY_PAD || from instanceof CarpetBlock) && !fromDown.getFluidState().isEmpty()) { // to ascend here we'd have to break the block we are standing on return COST_INF; } double hardness = MovementHelper.getMiningDurationTicks(context, x, y + 2, z, toBreak, true); if (hardness >= COST_INF) { return COST_INF; } if (hardness != 0) { if (toBreakBlock == Blocks.LADDER || toBreakBlock == Blocks.VINE) { hardness = 0; // we won't actually need to break the ladder / vine because we're going to use it } else { BlockState check = context.get(x, y + 3, z); // the block on top of the one we're going to break, could it fall on us? if (check.getBlock() instanceof FallingBlock) { // see MovementAscend's identical check for breaking a falling block above our head if (srcUp == null) { srcUp = context.get(x, y + 1, z); } if (!(toBreakBlock instanceof FallingBlock) || !(srcUp.getBlock() instanceof FallingBlock)) { return COST_INF; } } // this is commented because it may have had a purpose, but it's very unclear what it was. it's from the minebot era. //if (!MovementHelper.canWalkOn(context, chkPos, check) || MovementHelper.canWalkThrough(context, chkPos, check)) {//if the block above where we want to break is not a full block, don't do it // TODO why does canWalkThrough mean this action is COST_INF? // FallingBlock makes sense, and !canWalkOn deals with weird cases like if it were lava // but I don't understand why canWalkThrough makes it impossible // return COST_INF; //} } } if (ladder) { return LADDER_UP_ONE_COST + hardness * 5; } else { return JUMP_ONE_BLOCK_COST + placeCost + context.jumpPenalty + hardness; } } public static boolean hasAgainst(CalculationContext context, int x, int y, int z) { return MovementHelper.isBlockNormalCube(context.get(x + 1, y, z)) || MovementHelper.isBlockNormalCube(context.get(x - 1, y, z)) || MovementHelper.isBlockNormalCube(context.get(x, y, z + 1)) || MovementHelper.isBlockNormalCube(context.get(x, y, z - 1)); } public static BlockPos getAgainst(CalculationContext context, BetterBlockPos vine) { if (MovementHelper.isBlockNormalCube(context.get(vine.north()))) { return vine.north(); } if (MovementHelper.isBlockNormalCube(context.get(vine.south()))) { return vine.south(); } if (MovementHelper.isBlockNormalCube(context.get(vine.east()))) { return vine.east(); } if (MovementHelper.isBlockNormalCube(context.get(vine.west()))) { return vine.west(); } return null; } @Override public MovementState updateState(MovementState state) { super.updateState(state); if (state.getStatus() != MovementStatus.RUNNING) { return state; } if (ctx.playerFeet().y < src.y) { return state.setStatus(MovementStatus.UNREACHABLE); } BlockState fromDown = BlockStateInterface.get(ctx, src); if (MovementHelper.isWater(fromDown) && MovementHelper.isWater(ctx, dest)) { // stay centered while swimming up a water column state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations()), false)); Vec3 destCenter = VecUtils.getBlockPosCenter(dest); if (Math.abs(ctx.player().position().x - destCenter.x) > 0.2 || Math.abs(ctx.player().position().z - destCenter.z) > 0.2) { state.setInput(Input.MOVE_FORWARD, true); } if (ctx.playerFeet().equals(dest)) { return state.setStatus(MovementStatus.SUCCESS); } return state; } boolean ladder = fromDown.getBlock() == Blocks.LADDER || fromDown.getBlock() == Blocks.VINE; boolean vine = fromDown.getBlock() == Blocks.VINE; Rotation rotation = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(positionToPlace), ctx.playerRotations()); if (!ladder) { state.setTarget(new MovementState.MovementTarget(ctx.playerRotations().withPitch(rotation.getPitch()), true)); } boolean blockIsThere = MovementHelper.canWalkOn(ctx, src) || ladder; if (ladder) { BlockPos against = vine ? getAgainst(new CalculationContext(baritone), src) : src.relative(fromDown.getValue(LadderBlock.FACING).getOpposite()); if (against == null) { logDirect("Unable to climb vines. Consider disabling allowVines."); return state.setStatus(MovementStatus.UNREACHABLE); } if (ctx.playerFeet().equals(against.above()) || ctx.playerFeet().equals(dest)) { return state.setStatus(MovementStatus.SUCCESS); } if (MovementHelper.isBottomSlab(BlockStateInterface.get(ctx, src.below()))) { state.setInput(Input.JUMP, true); } /* if (thePlayer.getPosition0().getX() != from.getX() || thePlayer.getPosition0().getZ() != from.getZ()) { Baritone.moveTowardsBlock(from); } */ MovementHelper.moveTowards(ctx, state, against); return state; } else { // Get ready to place a throwaway block if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, src.x, src.y, src.z)) { return state.setStatus(MovementStatus.UNREACHABLE); } state.setInput(Input.SNEAK, true); // since (lower down) we only right click once player.isSneaking, and that happens the tick after we request to sneak double diffX = ctx.player().position().x - (dest.getX() + 0.5); double diffZ = ctx.player().position().z - (dest.getZ() + 0.5); double dist = Math.sqrt(diffX * diffX + diffZ * diffZ); double flatMotion = Math.sqrt(ctx.player().getDeltaMovement().x * ctx.player().getDeltaMovement().x + ctx.player().getDeltaMovement().z * ctx.player().getDeltaMovement().z); if (dist > 0.17) {//why 0.17? because it seemed like a good number, that's why //[explanation added after baritone port lol] also because it needs to be less than 0.2 because of the 0.3 sneak limit //and 0.17 is reasonably less than 0.2 // If it's been more than forty ticks of trying to jump and we aren't done yet, go forward, maybe we are stuck state.setInput(Input.MOVE_FORWARD, true); // revise our target to both yaw and pitch if we're going to be moving forward state.setTarget(new MovementState.MovementTarget(rotation, true)); } else if (flatMotion < 0.05) { // If our Y coordinate is above our goal, stop jumping state.setInput(Input.JUMP, ctx.player().position().y < dest.getY()); } if (!blockIsThere) { BlockState frState = BlockStateInterface.get(ctx, src); Block fr = frState.getBlock(); // TODO: Evaluate usage of getMaterial().isReplaceable() if (!(fr instanceof AirBlock || frState.getMaterial().isReplaceable())) { RotationUtils.reachable(ctx, src, ctx.playerController().getBlockReachDistance()) .map(rot -> new MovementState.MovementTarget(rot, true)) .ifPresent(state::setTarget); state.setInput(Input.JUMP, false); // breaking is like 5x slower when you're jumping state.setInput(Input.CLICK_LEFT, true); blockIsThere = false; } else if (ctx.player().isCrouching() && (ctx.isLookingAt(src.below()) || ctx.isLookingAt(src)) && ctx.player().position().y > dest.getY() + 0.1) { state.setInput(Input.CLICK_RIGHT, true); } } } // If we are at our goal and the block below us is placed if (ctx.playerFeet().equals(dest) && blockIsThere) { return state.setStatus(MovementStatus.SUCCESS); } return state; } @Override protected boolean prepared(MovementState state) { if (ctx.playerFeet().equals(src) || ctx.playerFeet().equals(src.below())) { Block block = BlockStateInterface.getBlock(ctx, src.below()); if (block == Blocks.LADDER || block == Blocks.VINE) { state.setInput(Input.SNEAK, true); } } if (MovementHelper.isWater(ctx, dest.above())) { return true; } return super.prepared(state); } } ================================================ FILE: src/main/java/baritone/pathing/movement/movements/MovementTraverse.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement.movements; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.VecUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import com.google.common.collect.ImmutableSet; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.CarpetBlock; import net.minecraft.world.level.block.DoorBlock; import net.minecraft.world.level.block.FenceGateBlock; import net.minecraft.world.level.block.LadderBlock; import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.SlabType; import net.minecraft.world.phys.Vec3; import java.util.Optional; import java.util.Set; public class MovementTraverse extends Movement { /** * Did we have to place a bridge block or was it always there */ private boolean wasTheBridgeBlockAlwaysThere = true; public MovementTraverse(IBaritone baritone, BetterBlockPos from, BetterBlockPos to) { super(baritone, from, to, new BetterBlockPos[]{to.above(), to}, to.below()); } @Override public void reset() { super.reset(); wasTheBridgeBlockAlwaysThere = true; } @Override public double calculateCost(CalculationContext context) { return cost(context, src.x, src.y, src.z, dest.x, dest.z); } @Override protected Set<BetterBlockPos> calculateValidPositions() { return ImmutableSet.of(src, dest); // src.above means that we don't get caught in an infinite loop in water } public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { BlockState pb0 = context.get(destX, y + 1, destZ); BlockState pb1 = context.get(destX, y, destZ); BlockState destOn = context.get(destX, y - 1, destZ); BlockState srcDown = context.get(x, y - 1, z); Block srcDownBlock = srcDown.getBlock(); boolean standingOnABlock = MovementHelper.mustBeSolidToWalkOn(context, x, y - 1, z, srcDown); boolean frostWalker = standingOnABlock && !context.assumeWalkOnWater && MovementHelper.canUseFrostWalker(context, destOn); if (frostWalker || MovementHelper.canWalkOn(context, destX, y - 1, destZ, destOn)) { //this is a walk, not a bridge double WC = WALK_ONE_BLOCK_COST; boolean water = false; boolean sneaking = false; if (MovementHelper.isWater(pb0) || MovementHelper.isWater(pb1)) { WC = context.waterWalkSpeed; water = true; } else { if (destOn.getBlock() == Blocks.SOUL_SAND) { WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } else if (frostWalker) { // with frostwalker we can walk on water without the penalty, if we are sure we won't be using jesus } else if (destOn.getBlock() == Blocks.WATER) { WC += context.walkOnWaterOnePenalty; } if (srcDownBlock == Blocks.SOUL_SAND) { WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } else if (context.allowWalkOnMagmaBlocks && srcDownBlock.equals(Blocks.MAGMA_BLOCK)) { sneaking = true; WC += (SNEAK_ONE_BLOCK_COST - WALK_ONE_BLOCK_COST) / 2; } } double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); if (hardness1 >= COST_INF) { return COST_INF; } double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); // only include falling on the upper block to break if (hardness1 == 0 && hardness2 == 0) { if (!water && !sneaking && context.canSprint) { // If there's nothing in the way, and this isn't water, and we aren't sneak placing // We can sprint =D // Don't check for soul sand, since we can sprint on that too WC *= SPRINT_MULTIPLIER; } return WC; } if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE) { hardness1 *= 5; hardness2 *= 5; } return WC + hardness1 + hardness2; } else {//this is a bridge, so we need to place a block if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE) { return COST_INF; } if (MovementHelper.isReplaceable(destX, y - 1, destZ, destOn, context.bsi)) { boolean throughWater = MovementHelper.isWater(pb0) || MovementHelper.isWater(pb1); if (MovementHelper.isWater(destOn) && throughWater) { // this happens when assume walk on water is true and this is a traverse in water, which isn't allowed return COST_INF; } double placeCost = context.costOfPlacingAt(destX, y - 1, destZ, destOn); if (placeCost >= COST_INF) { return COST_INF; } double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); if (hardness1 >= COST_INF) { return COST_INF; } double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); // only include falling on the upper block to break double WC = throughWater ? context.waterWalkSpeed : WALK_ONE_BLOCK_COST; for (int i = 0; i < 5; i++) { int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getStepX(); int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getStepY(); int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getStepZ(); if (againstX == x && againstZ == z) { // this would be a backplace continue; } if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) { // found a side place option return WC + placeCost + hardness1 + hardness2; } } // now that we've checked all possible directions to side place, we actually need to backplace if (srcDownBlock == Blocks.SOUL_SAND || (srcDownBlock instanceof SlabBlock && srcDown.getValue(SlabBlock.TYPE) != SlabType.DOUBLE)) { return COST_INF; // can't sneak and backplace against soul sand or half slabs (regardless of whether it's top half or bottom half) =/ } if (!standingOnABlock) { // standing on water / swimming return COST_INF; // this is obviously impossible } Block blockSrc = context.getBlock(x, y, z); if ((blockSrc == Blocks.LILY_PAD || blockSrc instanceof CarpetBlock) && !srcDown.getFluidState().isEmpty()) { return COST_INF; // we can stand on these but can't place against them } WC = WC * (SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST);//since we are sneak backplacing, we are sneaking lol return WC + placeCost + hardness1 + hardness2; } return COST_INF; } } @Override public MovementState updateState(MovementState state) { super.updateState(state); BlockState pb0 = BlockStateInterface.get(ctx, positionsToBreak[0]); BlockState pb1 = BlockStateInterface.get(ctx, positionsToBreak[1]); if (state.getStatus() != MovementStatus.RUNNING) { // if the setting is enabled if (!Baritone.settings().walkWhileBreaking.value) { return state; } // and if we're prepping (aka mining the block in front) if (state.getStatus() != MovementStatus.PREPPING) { return state; } // and if it's fine to walk into the blocks in front if (MovementHelper.avoidWalkingInto(pb0)) { return state; } if (MovementHelper.avoidWalkingInto(pb1)) { return state; } // and we aren't already pressed up against the block double dist = Math.max(Math.abs(ctx.player().position().x - (dest.getX() + 0.5D)), Math.abs(ctx.player().position().z - (dest.getZ() + 0.5D))); if (dist < 0.83) { return state; } if (!state.getTarget().getRotation().isPresent()) { // this can happen rarely when the server lags and doesn't send the falling sand entity until you've already walked through the block and are now mining the next one return state; } // combine the yaw to the center of the destination, and the pitch to the specific block we're trying to break // it's safe to do this since the two blocks we break (in a traverse) are right on top of each other and so will have the same yaw float yawToDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), dest), ctx.playerRotations()).getYaw(); float pitchToBreak = state.getTarget().getRotation().get().getPitch(); if ((MovementHelper.isBlockNormalCube(pb0) || pb0.getBlock() instanceof AirBlock && (MovementHelper.isBlockNormalCube(pb1) || pb1.getBlock() instanceof AirBlock))) { // in the meantime, before we're right up against the block, we can break efficiently at this angle pitchToBreak = 26; } return state.setTarget(new MovementState.MovementTarget(new Rotation(yawToDest, pitchToBreak), true)) .setInput(Input.MOVE_FORWARD, true) .setInput(Input.SPRINT, true); } Block fd = BlockStateInterface.get(ctx, src.below()).getBlock(); boolean ladder = fd == Blocks.LADDER || fd == Blocks.VINE; //sneak may have been set to true in the PREPPING state while mining an adjacent block, but we still want it to be true if the player is about to go on magma state.setInput(Input.SNEAK, Baritone.settings().allowWalkOnMagmaBlocks.value && MovementHelper.steppingOnBlocks(ctx).stream().anyMatch(block -> ctx.world().getBlockState(block).is(Blocks.MAGMA_BLOCK))); if (pb0.getBlock() instanceof DoorBlock || pb1.getBlock() instanceof DoorBlock) { boolean notPassable = pb0.getBlock() instanceof DoorBlock && !MovementHelper.isDoorPassable(ctx, src, dest) || pb1.getBlock() instanceof DoorBlock && !MovementHelper.isDoorPassable(ctx, dest, src); boolean canOpen = !(Blocks.IRON_DOOR.equals(pb0.getBlock()) || Blocks.IRON_DOOR.equals(pb1.getBlock())); if (notPassable && canOpen) { return state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), positionsToBreak[0]), ctx.playerRotations()), true)) .setInput(Input.CLICK_RIGHT, true); } } if (pb0.getBlock() instanceof FenceGateBlock || pb1.getBlock() instanceof FenceGateBlock) { BlockPos blocked = !MovementHelper.isGatePassable(ctx, positionsToBreak[0], src.above()) ? positionsToBreak[0] : !MovementHelper.isGatePassable(ctx, positionsToBreak[1], src) ? positionsToBreak[1] : null; if (blocked != null) { Optional<Rotation> rotation = RotationUtils.reachable(ctx, blocked); if (rotation.isPresent()) { return state.setTarget(new MovementState.MovementTarget(rotation.get(), true)).setInput(Input.CLICK_RIGHT, true); } } } boolean isTheBridgeBlockThere = MovementHelper.canWalkOn(ctx, positionToPlace) || ladder || MovementHelper.canUseFrostWalker(ctx, positionToPlace); BlockPos feet = ctx.playerFeet(); if (feet.getY() != dest.getY() && !ladder) { logDebug("Wrong Y coordinate"); if (feet.getY() < dest.getY()) { System.out.println("In movement traverse"); return state.setInput(Input.JUMP, true); } return state; } if (isTheBridgeBlockThere) { if (feet.equals(dest)) { return state.setStatus(MovementStatus.SUCCESS); } if (Baritone.settings().overshootTraverse.value && (feet.equals(dest.offset(getDirection())) || feet.equals(dest.offset(getDirection()).offset(getDirection())))) { return state.setStatus(MovementStatus.SUCCESS); } Block low = BlockStateInterface.get(ctx, src).getBlock(); Block high = BlockStateInterface.get(ctx, src.above()).getBlock(); if (ctx.player().position().y > src.y + 0.1D && !ctx.player().isOnGround() && (low == Blocks.VINE || low == Blocks.LADDER || high == Blocks.VINE || high == Blocks.LADDER)) { // hitting W could cause us to climb the ladder instead of going forward // wait until we're on the ground return state; } BlockPos into = dest.subtract(src).offset(dest); BlockState intoBelow = BlockStateInterface.get(ctx, into); BlockState intoAbove = BlockStateInterface.get(ctx, into.above()); if (wasTheBridgeBlockAlwaysThere && (!MovementHelper.isLiquid(ctx, feet) || Baritone.settings().sprintInWater.value) && (!MovementHelper.avoidWalkingInto(intoBelow) || MovementHelper.isWater(intoBelow)) && !MovementHelper.avoidWalkingInto(intoAbove)) { state.setInput(Input.SPRINT, true); } BlockState destDown = BlockStateInterface.get(ctx, dest.below()); BlockPos against = positionsToBreak[0]; if (feet.getY() != dest.getY() && ladder && (destDown.getBlock() == Blocks.VINE || destDown.getBlock() == Blocks.LADDER)) { against = destDown.getBlock() == Blocks.VINE ? MovementPillar.getAgainst(new CalculationContext(baritone), dest.below()) : dest.relative(destDown.getValue(LadderBlock.FACING).getOpposite()); if (against == null) { logDirect("Unable to climb vines. Consider disabling allowVines."); return state.setStatus(MovementStatus.UNREACHABLE); } } MovementHelper.moveTowards(ctx, state, against); return state; } else { wasTheBridgeBlockAlwaysThere = false; Block standingOn = BlockStateInterface.get(ctx, feet.below()).getBlock(); if (standingOn.equals(Blocks.SOUL_SAND) || standingOn instanceof SlabBlock) { // see issue #118 double dist = Math.max(Math.abs(dest.getX() + 0.5 - ctx.player().position().x), Math.abs(dest.getZ() + 0.5 - ctx.player().position().z)); if (dist < 0.85) { // 0.5 + 0.3 + epsilon MovementHelper.moveTowards(ctx, state, dest); return state.setInput(Input.MOVE_FORWARD, false) .setInput(Input.MOVE_BACK, true); } } double dist1 = Math.max(Math.abs(ctx.player().position().x - (dest.getX() + 0.5D)), Math.abs(ctx.player().position().z - (dest.getZ() + 0.5D))); PlaceResult p = MovementHelper.attemptToPlaceABlock(state, baritone, dest.below(), false, !Baritone.settings().assumeSafeWalk.value); if ((p == PlaceResult.READY_TO_PLACE || dist1 < 0.6) && !Baritone.settings().assumeSafeWalk.value) { state.setInput(Input.SNEAK, true); } switch (p) { case READY_TO_PLACE: { if (ctx.player().isCrouching() || Baritone.settings().assumeSafeWalk.value) { state.setInput(Input.CLICK_RIGHT, true); } return state; } case ATTEMPTING: { if (dist1 > 0.83) { // might need to go forward a bit float yaw = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations()).getYaw(); if (Math.abs(state.getTarget().rotation.getYaw() - yaw) < 0.1) { // but only if our attempted place is straight ahead return state.setInput(Input.MOVE_FORWARD, true); } } else if (ctx.playerRotations().isReallyCloseTo(state.getTarget().rotation)) { // well i guess theres something in the way return state.setInput(Input.CLICK_LEFT, true); } return state; } default: break; } if (feet.equals(dest)) { // If we are in the block that we are trying to get to, we are sneaking over air and we need to place a block beneath us against the one we just walked off of // Out.log(from + " " + to + " " + faceX + "," + faceY + "," + faceZ + " " + whereAmI); double faceX = (dest.getX() + src.getX() + 1.0D) * 0.5D; double faceY = (dest.getY() + src.getY() - 1.0D) * 0.5D; double faceZ = (dest.getZ() + src.getZ() + 1.0D) * 0.5D; // faceX, faceY, faceZ is the middle of the face between from and to BlockPos goalLook = src.below(); // this is the block we were just standing on, and the one we want to place against Rotation backToFace = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3(faceX, faceY, faceZ), ctx.playerRotations()); float pitch = backToFace.getPitch(); double dist2 = Math.max(Math.abs(ctx.player().position().x - faceX), Math.abs(ctx.player().position().z - faceZ)); if (dist2 < 0.29) { // see issue #208 float yaw = RotationUtils.calcRotationFromVec3d(VecUtils.getBlockPosCenter(dest), ctx.playerHead(), ctx.playerRotations()).getYaw(); state.setTarget(new MovementState.MovementTarget(new Rotation(yaw, pitch), true)); state.setInput(Input.MOVE_BACK, true); } else { state.setTarget(new MovementState.MovementTarget(backToFace, true)); } if (ctx.isLookingAt(goalLook)) { return state.setInput(Input.CLICK_RIGHT, true); // wait to right click until we are able to place } // Out.log("Trying to look at " + goalLook + ", actually looking at" + Baritone.whatAreYouLookingAt()); if (ctx.playerRotations().isReallyCloseTo(state.getTarget().rotation)) { state.setInput(Input.CLICK_LEFT, true); } return state; } MovementHelper.moveTowardsWithSlightRotation(ctx, state, dest); return state; } } @Override public boolean safeToCancel(MovementState state) { // if we're in the process of breaking blocks before walking forwards // or if this isn't a sneak place (the block is already there) // then it's safe to cancel this return state.getStatus() != MovementStatus.RUNNING || MovementHelper.canWalkOn(ctx, dest.below()); } @Override protected boolean prepared(MovementState state) { if (ctx.playerFeet().equals(src) || ctx.playerFeet().equals(src.below())) { Block block = BlockStateInterface.getBlock(ctx, src.below()); if (block == Blocks.LADDER || block == Blocks.VINE) { state.setInput(Input.SNEAK, true); } } return super.prepared(state); } } ================================================ FILE: src/main/java/baritone/pathing/path/CutoffPath.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.path; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.IMovement; import baritone.api.utils.BetterBlockPos; import baritone.utils.pathing.PathBase; import java.util.Collections; import java.util.List; public class CutoffPath extends PathBase { private final List<BetterBlockPos> path; private final List<IMovement> movements; private final int numNodes; private final Goal goal; public CutoffPath(IPath prev, int firstPositionToInclude, int lastPositionToInclude) { path = prev.positions().subList(firstPositionToInclude, lastPositionToInclude + 1); movements = prev.movements().subList(firstPositionToInclude, lastPositionToInclude); numNodes = prev.getNumNodesConsidered(); goal = prev.getGoal(); sanityCheck(); } public CutoffPath(IPath prev, int lastPositionToInclude) { this(prev, 0, lastPositionToInclude); } @Override public Goal getGoal() { return goal; } @Override public List<IMovement> movements() { return Collections.unmodifiableList(movements); } @Override public List<BetterBlockPos> positions() { return Collections.unmodifiableList(path); } @Override public int getNumNodesConsidered() { return numNodes; } } ================================================ FILE: src/main/java/baritone/pathing/path/PathExecutor.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.path; import baritone.Baritone; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.movement.ActionCosts; import baritone.api.pathing.movement.IMovement; import baritone.api.pathing.movement.MovementStatus; import baritone.api.pathing.path.IPathExecutor; import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.behavior.PathingBehavior; import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.movements.*; import baritone.utils.BlockStateInterface; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.util.Tuple; import net.minecraft.world.phys.Vec3; import java.util.*; import static baritone.api.pathing.movement.MovementStatus.*; /** * Behavior to execute a precomputed path * * @author leijurv */ public class PathExecutor implements IPathExecutor, Helper { private static final double MAX_MAX_DIST_FROM_PATH = 3; private static final double MAX_DIST_FROM_PATH = 2; /** * Default value is equal to 10 seconds. It's find to decrease it, but it must be at least 5.5s (110 ticks). * For more information, see issue #102. * * @see <a href="https://github.com/cabaletta/baritone/issues/102">Issue #102</a> * @see <a href="https://i.imgur.com/5s5GLnI.png">Anime</a> */ private static final double MAX_TICKS_AWAY = 200; private final IPath path; private int pathPosition; private int ticksAway; private int ticksOnCurrent; private Double currentMovementOriginalCostEstimate; private Integer costEstimateIndex; private boolean failed; private boolean recalcBP = true; private HashSet<BlockPos> toBreak = new HashSet<>(); private HashSet<BlockPos> toPlace = new HashSet<>(); private HashSet<BlockPos> toWalkInto = new HashSet<>(); private final PathingBehavior behavior; private final IPlayerContext ctx; private boolean sprintNextTick; public PathExecutor(PathingBehavior behavior, IPath path) { this.behavior = behavior; this.ctx = behavior.ctx; this.path = path; this.pathPosition = 0; } /** * Tick this executor * * @return True if a movement just finished (and the player is therefore in a "stable" state, like, * not sneaking out over lava), false otherwise */ public boolean onTick() { if (pathPosition == path.length() - 1) { pathPosition++; } if (pathPosition >= path.length()) { return true; // stop bugging me, I'm done } Movement movement = (Movement) path.movements().get(pathPosition); BetterBlockPos whereAmI = ctx.playerFeet(); if (!movement.getValidPositions().contains(whereAmI)) { for (int i = 0; i < pathPosition && i < path.length(); i++) {//this happens for example when you lag out and get teleported back a couple blocks if (((Movement) path.movements().get(i)).getValidPositions().contains(whereAmI)) { int previousPos = pathPosition; pathPosition = i; for (int j = pathPosition; j <= previousPos; j++) { path.movements().get(j).reset(); } onChangeInPathPosition(); onTick(); return false; } } for (int i = pathPosition + 3; i < path.length() - 1; i++) { //dont check pathPosition+1. the movement tells us when it's done (e.g. sneak placing) // also don't check pathPosition+2 because reasons if (((Movement) path.movements().get(i)).getValidPositions().contains(whereAmI)) { if (i - pathPosition > 2) { logDebug("Skipping forward " + (i - pathPosition) + " steps, to " + i); } //System.out.println("Double skip sundae"); pathPosition = i - 1; onChangeInPathPosition(); onTick(); return false; } } } Tuple<Double, BlockPos> status = closestPathPos(path); if (possiblyOffPath(status, MAX_DIST_FROM_PATH)) { ticksAway++; System.out.println("FAR AWAY FROM PATH FOR " + ticksAway + " TICKS. Current distance: " + status.getA() + ". Threshold: " + MAX_DIST_FROM_PATH); if (ticksAway > MAX_TICKS_AWAY) { logDebug("Too far away from path for too long, cancelling path"); cancel(); return false; } } else { ticksAway = 0; } if (possiblyOffPath(status, MAX_MAX_DIST_FROM_PATH)) { // ok, stop right away, we're way too far. logDebug("too far from path"); cancel(); return false; } //long start = System.nanoTime() / 1000000L; BlockStateInterface bsi = new BlockStateInterface(ctx); for (int i = pathPosition - 10; i < pathPosition + 10; i++) { if (i < 0 || i >= path.movements().size()) { continue; } Movement m = (Movement) path.movements().get(i); List<BlockPos> prevBreak = m.toBreak(bsi); List<BlockPos> prevPlace = m.toPlace(bsi); List<BlockPos> prevWalkInto = m.toWalkInto(bsi); m.resetBlockCache(); if (!prevBreak.equals(m.toBreak(bsi))) { recalcBP = true; } if (!prevPlace.equals(m.toPlace(bsi))) { recalcBP = true; } if (!prevWalkInto.equals(m.toWalkInto(bsi))) { recalcBP = true; } } if (recalcBP) { HashSet<BlockPos> newBreak = new HashSet<>(); HashSet<BlockPos> newPlace = new HashSet<>(); HashSet<BlockPos> newWalkInto = new HashSet<>(); for (int i = pathPosition; i < path.movements().size(); i++) { Movement m = (Movement) path.movements().get(i); newBreak.addAll(m.toBreak(bsi)); newPlace.addAll(m.toPlace(bsi)); newWalkInto.addAll(m.toWalkInto(bsi)); } toBreak = newBreak; toPlace = newPlace; toWalkInto = newWalkInto; recalcBP = false; } /*long end = System.nanoTime() / 1000000L; if (end - start > 0) { System.out.println("Recalculating break and place took " + (end - start) + "ms"); }*/ if (pathPosition < path.movements().size() - 1) { IMovement next = path.movements().get(pathPosition + 1); if (!behavior.baritone.bsi.worldContainsLoadedChunk(next.getDest().x, next.getDest().z)) { logDebug("Pausing since destination is at edge of loaded chunks"); clearKeys(); return true; } } boolean canCancel = movement.safeToCancel(); if (costEstimateIndex == null || costEstimateIndex != pathPosition) { costEstimateIndex = pathPosition; // do this only once, when the movement starts, and deliberately get the cost as cached when this path was calculated, not the cost as it is right now currentMovementOriginalCostEstimate = movement.getCost(); for (int i = 1; i < Baritone.settings().costVerificationLookahead.value && pathPosition + i < path.length() - 1; i++) { if (((Movement) path.movements().get(pathPosition + i)).calculateCost(behavior.secretInternalGetCalculationContext()) >= ActionCosts.COST_INF && canCancel) { logDebug("Something has changed in the world and a future movement has become impossible. Cancelling."); cancel(); return true; } } } double currentCost = movement.recalculateCost(behavior.secretInternalGetCalculationContext()); if (currentCost >= ActionCosts.COST_INF && canCancel) { logDebug("Something has changed in the world and this movement has become impossible. Cancelling."); cancel(); return true; } if (!movement.calculatedWhileLoaded() && currentCost - currentMovementOriginalCostEstimate > Baritone.settings().maxCostIncrease.value && canCancel) { // don't do this if the movement was calculated while loaded // that means that this isn't a cache error, it's just part of the path interfering with a later part logDebug("Original cost " + currentMovementOriginalCostEstimate + " current cost " + currentCost + ". Cancelling."); cancel(); return true; } if (shouldPause()) { logDebug("Pausing since current best path is a backtrack"); clearKeys(); return true; } MovementStatus movementStatus = movement.update(); if (movementStatus == UNREACHABLE || movementStatus == FAILED) { logDebug("Movement returns status " + movementStatus); cancel(); return true; } if (movementStatus == SUCCESS) { //System.out.println("Movement done, next path"); pathPosition++; onChangeInPathPosition(); onTick(); return true; } else { sprintNextTick = shouldSprintNextTick(); if (!sprintNextTick) { ctx.player().setSprinting(false); // letting go of control doesn't make you stop sprinting actually } ticksOnCurrent++; if (ticksOnCurrent > currentMovementOriginalCostEstimate + Baritone.settings().movementTimeoutTicks.value) { // only cancel if the total time has exceeded the initial estimate // as you break the blocks required, the remaining cost goes down, to the point where // ticksOnCurrent is greater than recalculateCost + 100 // this is why we cache cost at the beginning, and don't recalculate for this comparison every tick logDebug("This movement has taken too long (" + ticksOnCurrent + " ticks, expected " + currentMovementOriginalCostEstimate + "). Cancelling."); cancel(); return true; } } return canCancel; // movement is in progress, but if it reports cancellable, PathingBehavior is good to cut onto the next path } private Tuple<Double, BlockPos> closestPathPos(IPath path) { double best = -1; BlockPos bestPos = null; for (IMovement movement : path.movements()) { for (BlockPos pos : ((Movement) movement).getValidPositions()) { double dist = VecUtils.entityDistanceToCenter(ctx.player(), pos); if (dist < best || best == -1) { best = dist; bestPos = pos; } } } return new Tuple<>(best, bestPos); } private boolean shouldPause() { Optional<AbstractNodeCostSearch> current = behavior.getInProgress(); if (!current.isPresent()) { return false; } if (!ctx.player().isOnGround()) { return false; } if (!MovementHelper.canWalkOn(ctx, ctx.playerFeet().below())) { // we're in some kind of sketchy situation, maybe parkouring return false; } if (!MovementHelper.canWalkThrough(ctx, ctx.playerFeet()) || !MovementHelper.canWalkThrough(ctx, ctx.playerFeet().above())) { // suffocating? return false; } if (!path.movements().get(pathPosition).safeToCancel()) { return false; } Optional<IPath> currentBest = current.get().bestPathSoFar(); if (!currentBest.isPresent()) { return false; } List<BetterBlockPos> positions = currentBest.get().positions(); if (positions.size() < 3) { return false; // not long enough yet to justify pausing, its far from certain we'll actually take this route } // the first block of the next path will always overlap // no need to pause our very last movement when it would have otherwise cleanly exited with MovementStatus SUCCESS positions = positions.subList(1, positions.size()); return positions.contains(ctx.playerFeet()); } private boolean possiblyOffPath(Tuple<Double, BlockPos> status, double leniency) { double distanceFromPath = status.getA(); if (distanceFromPath > leniency) { // when we're midair in the middle of a fall, we're very far from both the beginning and the end, but we aren't actually off path if (path.movements().get(pathPosition) instanceof MovementFall) { BlockPos fallDest = path.positions().get(pathPosition + 1); // .get(pathPosition) is the block we fell off of return VecUtils.entityFlatDistanceToCenter(ctx.player(), fallDest) >= leniency; // ignore Y by using flat distance } else { return true; } } else { return false; } } /** * Regardless of current path position, snap to the current player feet if possible * * @return Whether or not it was possible to snap to the current player feet */ public boolean snipsnapifpossible() { if (!ctx.player().isOnGround() && ctx.world().getFluidState(ctx.playerFeet()).isEmpty()) { // if we're falling in the air, and not in water, don't splice return false; } else { // we are either onGround or in liquid if (ctx.player().getDeltaMovement().y < -0.1) { // if we are strictly moving downwards (not stationary) // we could be falling through water, which could be unsafe to splice return false; // so don't } } int index = path.positions().indexOf(ctx.playerFeet()); if (index == -1) { return false; } pathPosition = index; // jump directly to current position clearKeys(); return true; } private boolean shouldSprintNextTick() { boolean requested = behavior.baritone.getInputOverrideHandler().isInputForcedDown(Input.SPRINT); // we'll take it from here, no need for minecraft to see we're holding down control and sprint for us behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false); // first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint if (!new CalculationContext(behavior.baritone, false).canSprint) { return false; } IMovement current = path.movements().get(pathPosition); // traverse requests sprinting, so we need to do this check first if (current instanceof MovementTraverse && pathPosition < path.length() - 3) { IMovement next = path.movements().get(pathPosition + 1); if (next instanceof MovementAscend && sprintableAscend(ctx, (MovementTraverse) current, (MovementAscend) next, path.movements().get(pathPosition + 2))) { if (skipNow(ctx, current)) { logDebug("Skipping traverse to straight ascend"); pathPosition++; onChangeInPathPosition(); onTick(); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); return true; } else { logDebug("Too far to the side to safely sprint ascend"); } } } // if the movement requested sprinting, then we're done if (requested) { return true; } // however, descend and ascend don't request sprinting, because they don't know the context of what movement comes after it if (current instanceof MovementDescend) { if (pathPosition < path.length() - 2) { // keep this out of onTick, even if that means a tick of delay before it has an effect IMovement next = path.movements().get(pathPosition + 1); if (MovementHelper.canUseFrostWalker(ctx, next.getDest().below())) { // frostwalker only works if you cross the edge of the block on ground so in some cases we may not overshoot // Since MovementDescend can't know the next movement we have to tell it if (next instanceof MovementTraverse || next instanceof MovementParkour) { boolean couldPlaceInstead = Baritone.settings().allowPlace.value && behavior.baritone.getInventoryBehavior().hasGenericThrowaway() && next instanceof MovementParkour; // traverse doesn't react fast enough // this is true if the next movement does not ascend or descends and goes into the same cardinal direction (N-NE-E-SE-S-SW-W-NW) as the descend // in that case current.getDirection() is e.g. (0, -1, 1) and next.getDirection() is e.g. (0, 0, 3) so the cross product of (0, 0, 1) and (0, 0, 3) is taken, which is (0, 0, 0) because the vectors are colinear (don't form a plane) // since movements in exactly the opposite direction (e.g. descend (0, -1, 1) and traverse (0, 0, -1)) would also pass this check we also have to rule out that case // we can do that by adding the directions because traverse is always 1 long like descend and parkour can't jump through current.getSrc().down() boolean sameFlatDirection = !current.getDirection().above().offset(next.getDirection()).equals(BlockPos.ZERO) && current.getDirection().above().cross(next.getDirection()).equals(BlockPos.ZERO); // here's why you learn maths in school if (sameFlatDirection && !couldPlaceInstead) { ((MovementDescend) current).forceSafeMode(); } } } } if (((MovementDescend) current).safeMode() && !((MovementDescend) current).skipToAscend()) { logDebug("Sprinting would be unsafe"); return false; } if (pathPosition < path.length() - 2) { IMovement next = path.movements().get(pathPosition + 1); if (next instanceof MovementAscend && current.getDirection().above().equals(next.getDirection().below())) { // a descend then an ascend in the same direction pathPosition++; onChangeInPathPosition(); onTick(); // okay to skip clearKeys and / or onChangeInPathPosition here since this isn't possible to repeat, since it's asymmetric logDebug("Skipping descend to straight ascend"); return true; } if (canSprintFromDescendInto(ctx, current, next)) { if (next instanceof MovementDescend && pathPosition < path.length() - 3) { IMovement next_next = path.movements().get(pathPosition + 2); if (next_next instanceof MovementDescend && !canSprintFromDescendInto(ctx, next, next_next)) { return false; } } if (ctx.playerFeet().equals(current.getDest())) { pathPosition++; onChangeInPathPosition(); onTick(); } return true; } //logDebug("Turning off sprinting " + movement + " " + next + " " + movement.getDirection() + " " + next.getDirection().down() + " " + next.getDirection().down().equals(movement.getDirection())); } } if (current instanceof MovementAscend && pathPosition != 0) { IMovement prev = path.movements().get(pathPosition - 1); if (prev instanceof MovementDescend && prev.getDirection().above().equals(current.getDirection().below())) { BlockPos center = current.getSrc().above(); // playerFeet adds 0.1251 to account for soul sand // farmland is 0.9375 // 0.07 is to account for farmland if (ctx.player().position().y >= center.getY() - 0.07) { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); return true; } } if (pathPosition < path.length() - 2 && prev instanceof MovementTraverse && sprintableAscend(ctx, (MovementTraverse) prev, (MovementAscend) current, path.movements().get(pathPosition + 1))) { return true; } } if (current instanceof MovementFall) { Tuple<Vec3, BlockPos> data = overrideFall((MovementFall) current); if (data != null) { BetterBlockPos fallDest = new BetterBlockPos(data.getB()); if (!path.positions().contains(fallDest)) { throw new IllegalStateException(String.format( "Fall override at %s %s %s returned illegal destination %s %s %s", current.getSrc(), fallDest)); } if (ctx.playerFeet().equals(fallDest)) { pathPosition = path.positions().indexOf(fallDest); onChangeInPathPosition(); onTick(); return true; } clearKeys(); behavior.baritone.getLookBehavior().updateTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), data.getA(), ctx.playerRotations()), false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); return true; } } return false; } private Tuple<Vec3, BlockPos> overrideFall(MovementFall movement) { Vec3i dir = movement.getDirection(); if (dir.getY() < -3) { return null; } if (!movement.toBreakCached.isEmpty()) { return null; // it's breaking } Vec3i flatDir = new Vec3i(dir.getX(), 0, dir.getZ()); int i; outer: for (i = pathPosition + 1; i < path.length() - 1 && i < pathPosition + 3; i++) { IMovement next = path.movements().get(i); if (!(next instanceof MovementTraverse)) { break; } if (!flatDir.equals(next.getDirection())) { break; } for (int y = next.getDest().y; y <= movement.getSrc().y + 1; y++) { BlockPos chk = new BlockPos(next.getDest().x, y, next.getDest().z); if (!MovementHelper.fullyPassable(ctx, chk)) { break outer; } } if (!MovementHelper.canWalkOn(ctx, next.getDest().below())) { break; } } i--; if (i == pathPosition) { return null; // no valid extension exists } double len = i - pathPosition - 0.4; return new Tuple<>( new Vec3(flatDir.getX() * len + movement.getDest().x + 0.5, movement.getDest().y, flatDir.getZ() * len + movement.getDest().z + 0.5), movement.getDest().offset(flatDir.getX() * (i - pathPosition), 0, flatDir.getZ() * (i - pathPosition))); } private static boolean skipNow(IPlayerContext ctx, IMovement current) { double offTarget = Math.abs(current.getDirection().getX() * (current.getSrc().z + 0.5D - ctx.player().position().z)) + Math.abs(current.getDirection().getZ() * (current.getSrc().x + 0.5D - ctx.player().position().x)); if (offTarget > 0.1) { return false; } // we are centered BlockPos headBonk = current.getSrc().subtract(current.getDirection()).above(2); if (MovementHelper.fullyPassable(ctx, headBonk)) { return true; } // wait 0.3 double flatDist = Math.abs(current.getDirection().getX() * (headBonk.getX() + 0.5D - ctx.player().position().x)) + Math.abs(current.getDirection().getZ() * (headBonk.getZ() + 0.5 - ctx.player().position().z)); return flatDist > 0.8; } private static boolean sprintableAscend(IPlayerContext ctx, MovementTraverse current, MovementAscend next, IMovement nextnext) { if (!Baritone.settings().sprintAscends.value) { return false; } if (!current.getDirection().equals(next.getDirection().below())) { return false; } if (nextnext.getDirection().getX() != next.getDirection().getX() || nextnext.getDirection().getZ() != next.getDirection().getZ()) { return false; } if (!MovementHelper.canWalkOn(ctx, current.getDest().below())) { return false; } if (!MovementHelper.canWalkOn(ctx, next.getDest().below())) { return false; } if (!next.toBreakCached.isEmpty()) { return false; // it's breaking } for (int x = 0; x < 2; x++) { for (int y = 0; y < 3; y++) { BlockPos chk = current.getSrc().above(y); if (x == 1) { chk = chk.offset(current.getDirection()); } if (!MovementHelper.fullyPassable(ctx, chk)) { return false; } } } if (MovementHelper.avoidWalkingInto(ctx.world().getBlockState(current.getSrc().above(3)))) { return false; } return !MovementHelper.avoidWalkingInto(ctx.world().getBlockState(next.getDest().above(2))); // codacy smh my head } private static boolean canSprintFromDescendInto(IPlayerContext ctx, IMovement current, IMovement next) { if (next instanceof MovementDescend && next.getDirection().equals(current.getDirection())) { return true; } if (!MovementHelper.canWalkOn(ctx, current.getDest().offset(current.getDirection()))) { return false; } if (next instanceof MovementTraverse && next.getDirection().equals(current.getDirection())) { return true; } return next instanceof MovementDiagonal && Baritone.settings().allowOvershootDiagonalDescend.value; } private void onChangeInPathPosition() { clearKeys(); ticksOnCurrent = 0; } private void clearKeys() { // i'm just sick and tired of this snippet being everywhere lol behavior.baritone.getInputOverrideHandler().clearAllKeys(); } private void cancel() { clearKeys(); behavior.baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); pathPosition = path.length() + 3; failed = true; } @Override public int getPosition() { return pathPosition; } public PathExecutor trySplice(PathExecutor next) { if (next == null) { return cutIfTooLong(); } return SplicedPath.trySplice(path, next.path, false).map(path -> { if (!path.getDest().equals(next.getPath().getDest())) { throw new IllegalStateException(String.format( "Path has end %s instead of %s after splicing", path.getDest(), next.getPath().getDest())); } PathExecutor ret = new PathExecutor(behavior, path); ret.pathPosition = pathPosition; ret.currentMovementOriginalCostEstimate = currentMovementOriginalCostEstimate; ret.costEstimateIndex = costEstimateIndex; ret.ticksOnCurrent = ticksOnCurrent; return ret; }).orElseGet(this::cutIfTooLong); // dont actually call cutIfTooLong every tick if we won't actually use it, use a method reference } private PathExecutor cutIfTooLong() { if (pathPosition > Baritone.settings().maxPathHistoryLength.value) { int cutoffAmt = Baritone.settings().pathHistoryCutoffAmount.value; CutoffPath newPath = new CutoffPath(path, cutoffAmt, path.length() - 1); if (!newPath.getDest().equals(path.getDest())) { throw new IllegalStateException(String.format( "Path has end %s instead of %s after trimming its start", newPath.getDest(), path.getDest())); } logDebug("Discarding earliest segment movements, length cut from " + path.length() + " to " + newPath.length()); PathExecutor ret = new PathExecutor(behavior, newPath); ret.pathPosition = pathPosition - cutoffAmt; ret.currentMovementOriginalCostEstimate = currentMovementOriginalCostEstimate; if (costEstimateIndex != null) { ret.costEstimateIndex = costEstimateIndex - cutoffAmt; } ret.ticksOnCurrent = ticksOnCurrent; return ret; } return this; } @Override public IPath getPath() { return path; } public boolean failed() { return failed; } public boolean finished() { return pathPosition >= path.length(); } public Set<BlockPos> toBreak() { return Collections.unmodifiableSet(toBreak); } public Set<BlockPos> toPlace() { return Collections.unmodifiableSet(toPlace); } public Set<BlockPos> toWalkInto() { return Collections.unmodifiableSet(toWalkInto); } public boolean isSprinting() { return sprintNextTick; } } ================================================ FILE: src/main/java/baritone/pathing/path/SplicedPath.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.path; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.IMovement; import baritone.api.utils.BetterBlockPos; import baritone.utils.pathing.PathBase; import java.util.*; public class SplicedPath extends PathBase { private final List<BetterBlockPos> path; private final List<IMovement> movements; private final int numNodes; private final Goal goal; private SplicedPath(List<BetterBlockPos> path, List<IMovement> movements, int numNodesConsidered, Goal goal) { this.path = path; this.movements = movements; this.numNodes = numNodesConsidered; this.goal = goal; sanityCheck(); } @Override public Goal getGoal() { return goal; } @Override public List<IMovement> movements() { return Collections.unmodifiableList(movements); } @Override public List<BetterBlockPos> positions() { return Collections.unmodifiableList(path); } @Override public int getNumNodesConsidered() { return numNodes; } @Override public int length() { return path.size(); } public static Optional<SplicedPath> trySplice(IPath first, IPath second, boolean allowOverlapCutoff) { if (second == null || first == null) { return Optional.empty(); } if (!first.getDest().equals(second.getSrc())) { return Optional.empty(); } HashSet<BetterBlockPos> secondPos = new HashSet<>(second.positions()); int firstPositionInSecond = -1; for (int i = 0; i < first.length() - 1; i++) { // overlap in the very last element is fine (and required) so only go up to first.length() - 1 if (secondPos.contains(first.positions().get(i))) { firstPositionInSecond = i; break; } } if (firstPositionInSecond != -1) { if (!allowOverlapCutoff) { return Optional.empty(); } } else { firstPositionInSecond = first.length() - 1; } int positionInSecond = second.positions().indexOf(first.positions().get(firstPositionInSecond)); if (!allowOverlapCutoff && positionInSecond != 0) { throw new IllegalStateException("Paths to be spliced are overlapping incorrectly"); } List<BetterBlockPos> positions = new ArrayList<>(); List<IMovement> movements = new ArrayList<>(); positions.addAll(first.positions().subList(0, firstPositionInSecond + 1)); movements.addAll(first.movements().subList(0, firstPositionInSecond)); positions.addAll(second.positions().subList(positionInSecond + 1, second.length())); movements.addAll(second.movements().subList(positionInSecond, second.length() - 1)); return Optional.of(new SplicedPath(positions, movements, first.getNumNodesConsidered() + second.getNumNodesConsidered(), first.getGoal())); } } ================================================ FILE: src/main/java/baritone/pathing/precompute/PrecomputedData.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.precompute; import baritone.pathing.movement.MovementHelper; import baritone.utils.BlockStateInterface; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; public class PrecomputedData { private final byte[] data = new byte[Block.BLOCK_STATE_REGISTRY.size()]; /** * byte layout * * 7 6 5 4 3 2 1 0 * | | | | | | | | * unused canWalkOn maybe canWalkThrough maybe fullyPassable maybe completed */ private static final byte COMPLETED_MASK = (byte) 1 << 0; private static final byte FULLY_PASSABLE_MAYBE_MASK = (byte) 1 << 1; private static final byte FULLY_PASSABLE_MASK = (byte) 1 << 2; private static final byte CAN_WALK_THROUGH_MAYBE_MASK = (byte) 1 << 3; private static final byte CAN_WALK_THROUGH_MASK = (byte) 1 << 4; private static final byte CAN_WALK_ON_MAYBE_MASK = (byte) 1 << 5; private static final byte CAN_WALK_ON_MASK = (byte) 1 << 6; private int fillData(int id, BlockState state) { byte blockData = 0; Ternary canWalkOnState = MovementHelper.canWalkOnBlockState(state); switch (canWalkOnState) { case YES -> blockData |= CAN_WALK_ON_MASK; case MAYBE -> blockData |= CAN_WALK_ON_MAYBE_MASK; } Ternary canWalkThroughState = MovementHelper.canWalkThroughBlockState(state); switch (canWalkThroughState) { case YES -> blockData |= CAN_WALK_THROUGH_MASK; case MAYBE -> blockData |= CAN_WALK_THROUGH_MAYBE_MASK; } Ternary fullyPassableState = MovementHelper.fullyPassableBlockState(state); switch (fullyPassableState) { case YES -> blockData |= FULLY_PASSABLE_MASK; case MAYBE -> blockData |= FULLY_PASSABLE_MAYBE_MASK; } blockData |= COMPLETED_MASK; data[id] = blockData; // in theory, this is thread "safe" because every thread should compute the exact same int to write? return blockData; } public boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, BlockState state) { int id = Block.BLOCK_STATE_REGISTRY.getId(state); int blockData = data[id]; if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data blockData = fillData(id, state); } if ((blockData & CAN_WALK_ON_MAYBE_MASK) != 0) { return MovementHelper.canWalkOnPosition(bsi, x, y, z, state); } else { return (blockData & CAN_WALK_ON_MASK) != 0; } } public boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, BlockState state) { int id = Block.BLOCK_STATE_REGISTRY.getId(state); int blockData = data[id]; if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data blockData = fillData(id, state); } if ((blockData & CAN_WALK_THROUGH_MAYBE_MASK) != 0) { return MovementHelper.canWalkThroughPosition(bsi, x, y, z, state); } else { return (blockData & CAN_WALK_THROUGH_MASK) != 0; } } public boolean fullyPassable(BlockStateInterface bsi, int x, int y, int z, BlockState state) { int id = Block.BLOCK_STATE_REGISTRY.getId(state); int blockData = data[id]; if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data blockData = fillData(id, state); } if ((blockData & FULLY_PASSABLE_MAYBE_MASK) != 0) { return MovementHelper.fullyPassablePosition(bsi, x, y, z, state); } else { return (blockData & FULLY_PASSABLE_MASK) != 0; } } } ================================================ FILE: src/main/java/baritone/pathing/precompute/Ternary.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.precompute; public enum Ternary { YES, MAYBE, NO } ================================================ FILE: src/main/java/baritone/process/BackfillProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.utils.input.Input; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.pathing.path.PathExecutor; import baritone.utils.BaritoneProcessHelper; import java.util.*; import java.util.stream.Collectors; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.EmptyLevelChunk; public final class BackfillProcess extends BaritoneProcessHelper { public HashMap<BlockPos, BlockState> blocksToReplace = new HashMap<>(); public BackfillProcess(Baritone baritone) { super(baritone); } @Override public boolean isActive() { if (ctx.player() == null || ctx.world() == null) { return false; } if (!Baritone.settings().backfill.value) { return false; } if (Baritone.settings().allowParkour.value) { logDirect("Backfill cannot be used with allowParkour true"); Baritone.settings().backfill.value = false; return false; } for (BlockPos pos : new ArrayList<>(blocksToReplace.keySet())) { if (ctx.world().getChunk(pos) instanceof EmptyLevelChunk || ctx.world().getBlockState(pos).getBlock() != Blocks.AIR) { blocksToReplace.remove(pos); } } amIBreakingABlockHMMMMMMM(); baritone.getInputOverrideHandler().clearAllKeys(); return !toFillIn().isEmpty(); } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (!isSafeToCancel) { return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } baritone.getInputOverrideHandler().clearAllKeys(); for (BlockPos toPlace : toFillIn()) { MovementState fake = new MovementState(); switch (MovementHelper.attemptToPlaceABlock(fake, baritone, toPlace, false, false)) { case NO_OPTION: continue; case READY_TO_PLACE: baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); case ATTEMPTING: // patience baritone.getLookBehavior().updateTarget(fake.getTarget().getRotation().get(), true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); default: throw new IllegalStateException(); } } return new PathingCommand(null, PathingCommandType.DEFER); // cede to other process } private void amIBreakingABlockHMMMMMMM() { if (!ctx.getSelectedBlock().isPresent() || !baritone.getPathingBehavior().isPathing()) { return; } blocksToReplace.put(ctx.getSelectedBlock().get(), ctx.world().getBlockState(ctx.getSelectedBlock().get())); } public List<BlockPos> toFillIn() { return blocksToReplace .keySet() .stream() .filter(pos -> ctx.world().getBlockState(pos).getBlock() == Blocks.AIR) .filter(pos -> baritone.getBuilderProcess().placementPlausible(pos, Blocks.DIRT.defaultBlockState())) .filter(pos -> !partOfCurrentMovement(pos)) .sorted(Comparator.<BlockPos>comparingDouble(ctx.playerFeet()::distSqr).reversed()) .collect(Collectors.toList()); } private boolean partOfCurrentMovement(BlockPos pos) { PathExecutor exec = baritone.getPathingBehavior().getCurrent(); if (exec == null || exec.finished() || exec.failed()) { return false; } Movement movement = (Movement) exec.getPath().movements().get(exec.getPosition()); return Arrays.asList(movement.toBreakAll()).contains(pos); } @Override public void onLostControl() { if (blocksToReplace != null && !blocksToReplace.isEmpty()) { blocksToReplace.clear(); } } @Override public String displayName0() { return "Backfill"; } @Override public boolean isTemporary() { return true; } @Override public double priority() { return 5; } } ================================================ FILE: src/main/java/baritone/process/BuilderProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.pathing.goals.GoalComposite; import baritone.api.pathing.goals.GoalGetToBlock; import baritone.api.process.IBuilderProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.schematic.FillSchematic; import baritone.api.schematic.ISchematic; import baritone.api.schematic.IStaticSchematic; import baritone.api.schematic.MaskSchematic; import baritone.api.schematic.SubstituteSchematic; import baritone.api.schematic.RotatedSchematic; import baritone.api.schematic.MirroredSchematic; import baritone.api.schematic.format.ISchematicFormat; 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.MapArtSchematic; import baritone.utils.schematic.SelectionSchematic; import baritone.utils.schematic.SchematicSystem; import baritone.utils.schematic.litematica.LitematicaHelper; import baritone.utils.schematic.schematica.SchematicaHelper; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.util.Tuple; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.HorizontalDirectionalBlock; import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.PipeBlock; import net.minecraft.world.level.block.RotatedPillarBlock; import net.minecraft.world.level.block.StairBlock; import net.minecraft.world.level.block.TrapDoorBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import java.io.File; import java.io.FileInputStream; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import static baritone.api.pathing.movement.ActionCosts.COST_INF; public final class BuilderProcess extends BaritoneProcessHelper implements IBuilderProcess { private static final Set<Property<?>> ORIENTATION_PROPS = ImmutableSet.of( RotatedPillarBlock.AXIS, HorizontalDirectionalBlock.FACING, StairBlock.FACING, StairBlock.HALF, StairBlock.SHAPE, PipeBlock.NORTH, PipeBlock.EAST, PipeBlock.SOUTH, PipeBlock.WEST, PipeBlock.UP, TrapDoorBlock.OPEN, TrapDoorBlock.HALF ); private HashSet<BetterBlockPos> incorrectPositions; private LongOpenHashSet observedCompleted; // positions that are completed even if they're out of render distance and we can't make sure right now private String name; private ISchematic realSchematic; private ISchematic schematic; private Vec3i origin; private int ticks; private boolean paused; private int layer; private int numRepeats; private List<BlockState> approxPlaceable; public int stopAtHeight = 0; public BuilderProcess(Baritone baritone) { super(baritone); } @Override public void build(String name, ISchematic schematic, Vec3i origin) { this.name = name; this.schematic = schematic; this.realSchematic = null; boolean buildingSelectionSchematic = schematic instanceof SelectionSchematic; if (!Baritone.settings().buildSubstitutes.value.isEmpty()) { this.schematic = new SubstituteSchematic(this.schematic, Baritone.settings().buildSubstitutes.value); } if (Baritone.settings().buildSchematicMirror.value != net.minecraft.world.level.block.Mirror.NONE) { this.schematic = new MirroredSchematic(this.schematic, Baritone.settings().buildSchematicMirror.value); } if (Baritone.settings().buildSchematicRotation.value != net.minecraft.world.level.block.Rotation.NONE) { this.schematic = new RotatedSchematic(this.schematic, Baritone.settings().buildSchematicRotation.value); } // TODO this preserves the old behavior, but maybe we should bake the setting value right here this.schematic = new MaskSchematic(this.schematic) { @Override public boolean partOfMask(int x, int y, int z, BlockState current) { // partOfMask is only called inside the schematic so desiredState is not null return !Baritone.settings().buildSkipBlocks.value.contains(this.desiredState(x, y, z, current, Collections.emptyList()).getBlock()); } }; int x = origin.getX(); int y = origin.getY(); int z = origin.getZ(); if (Baritone.settings().schematicOrientationX.value) { x += schematic.widthX(); } if (Baritone.settings().schematicOrientationY.value) { y += schematic.heightY(); } if (Baritone.settings().schematicOrientationZ.value) { z += schematic.lengthZ(); } this.origin = new Vec3i(x, y, z); this.paused = false; this.layer = Baritone.settings().startAtLayer.value; this.stopAtHeight = schematic.heightY(); if (Baritone.settings().buildOnlySelection.value && buildingSelectionSchematic) { // currently redundant but safer maybe if (baritone.getSelectionManager().getSelections().length == 0) { logDirect("Poor little kitten forgot to set a selection while BuildOnlySelection is true"); this.stopAtHeight = 0; } else if (Baritone.settings().buildInLayers.value) { OptionalInt minim = Stream.of(baritone.getSelectionManager().getSelections()).mapToInt(sel -> sel.min().y).min(); OptionalInt maxim = Stream.of(baritone.getSelectionManager().getSelections()).mapToInt(sel -> sel.max().y).max(); if (minim.isPresent() && maxim.isPresent()) { int startAtHeight = Baritone.settings().layerOrder.value ? y + schematic.heightY() - maxim.getAsInt() : minim.getAsInt() - y; this.stopAtHeight = (Baritone.settings().layerOrder.value ? y + schematic.heightY() - minim.getAsInt() : maxim.getAsInt() - y) + 1; this.layer = Math.max(this.layer, startAtHeight / Baritone.settings().layerHeight.value); // startAtLayer or startAtHeight, whichever is highest logDebug(String.format("Schematic starts at y=%s with height %s", y, schematic.heightY())); logDebug(String.format("Selection starts at y=%s and ends at y=%s", minim.getAsInt(), maxim.getAsInt())); logDebug(String.format("Considering relevant height %s - %s", startAtHeight, this.stopAtHeight)); } } } this.numRepeats = 0; this.observedCompleted = new LongOpenHashSet(); this.incorrectPositions = null; } public void resume() { paused = false; } public void pause() { paused = true; } @Override public boolean isPaused() { return paused; } @Override public boolean build(String name, File schematic, Vec3i origin) { Optional<ISchematicFormat> format = SchematicSystem.INSTANCE.getByFile(schematic); if (!format.isPresent()) { return false; } IStaticSchematic parsed; try { parsed = format.get().parse(new FileInputStream(schematic)); } catch (Exception e) { e.printStackTrace(); return false; } ISchematic schem = applyMapArtAndSelection(origin, parsed); build(name, schem, origin); return true; } private ISchematic applyMapArtAndSelection(Vec3i origin, IStaticSchematic parsed) { ISchematic schematic = parsed; if (Baritone.settings().mapArtMode.value) { schematic = new MapArtSchematic(parsed); } if (Baritone.settings().buildOnlySelection.value) { schematic = new SelectionSchematic(schematic, origin, baritone.getSelectionManager().getSelections()); } return schematic; } @Override public void buildOpenSchematic() { if (SchematicaHelper.isSchematicaPresent()) { Optional<Tuple<IStaticSchematic, BlockPos>> schematic = SchematicaHelper.getOpenSchematic(); if (schematic.isPresent()) { IStaticSchematic raw = schematic.get().getA(); BlockPos origin = schematic.get().getB(); ISchematic schem = applyMapArtAndSelection(origin, raw); this.build(raw.toString(), schem, origin); } else { logDirect("No schematic currently open"); } } else { logDirect("Schematica is not present"); } } @Override public void buildOpenLitematic(int i) { if (LitematicaHelper.isLitematicaPresent()) { //if java.lang.NoSuchMethodError is thrown see comment in SchematicPlacementManager if (LitematicaHelper.hasLoadedSchematic(i)) { Tuple<IStaticSchematic, Vec3i> schematic = LitematicaHelper.getSchematic(i); Vec3i correctedOrigin = schematic.getB(); ISchematic schematic2 = applyMapArtAndSelection(correctedOrigin, schematic.getA()); build(schematic.getA().toString(), schematic2, correctedOrigin); } else { logDirect(String.format("List of placements has no entry %s", i + 1)); } } else { logDirect("Litematica is not present"); } } public void clearArea(BlockPos corner1, BlockPos corner2) { BlockPos origin = new BlockPos(Math.min(corner1.getX(), corner2.getX()), Math.min(corner1.getY(), corner2.getY()), Math.min(corner1.getZ(), corner2.getZ())); int widthX = Math.abs(corner1.getX() - corner2.getX()) + 1; int heightY = Math.abs(corner1.getY() - corner2.getY()) + 1; int lengthZ = Math.abs(corner1.getZ() - corner2.getZ()) + 1; build("clear area", new FillSchematic(widthX, heightY, lengthZ, Blocks.AIR.defaultBlockState()), origin); } @Override public List<BlockState> getApproxPlaceable() { return new ArrayList<>(approxPlaceable); } @Override public boolean isActive() { return schematic != null; } public BlockState placeAt(int x, int y, int z, BlockState current) { if (!isActive()) { return null; } if (!schematic.inSchematic(x - origin.getX(), y - origin.getY(), z - origin.getZ(), current)) { return null; } BlockState state = schematic.desiredState(x - origin.getX(), y - origin.getY(), z - origin.getZ(), current, this.approxPlaceable); if (state.getBlock() instanceof AirBlock) { return null; } return state; } private Optional<Tuple<BetterBlockPos, Rotation>> toBreakNearPlayer(BuilderCalculationContext bcc) { BetterBlockPos center = ctx.playerFeet(); BetterBlockPos pathStart = baritone.getPathingBehavior().pathStart(); for (int dx = -5; dx <= 5; dx++) { for (int dy = Baritone.settings().breakFromAbove.value ? -1 : 0; dy <= 5; dy++) { for (int dz = -5; dz <= 5; dz++) { int x = center.x + dx; int y = center.y + dy; int z = center.z + dz; if (dy == -1 && x == pathStart.x && z == pathStart.z) { continue; // dont mine what we're supported by, but not directly standing on } BlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired == null) { continue; // irrelevant } BlockState curr = bcc.bsi.get0(x, y, z); if (!(curr.getBlock() instanceof AirBlock) && !(curr.getBlock() == Blocks.WATER || curr.getBlock() == Blocks.LAVA) && !valid(curr, desired, false)) { BetterBlockPos pos = new BetterBlockPos(x, y, z); Optional<Rotation> rot = RotationUtils.reachable(ctx, pos, ctx.playerController().getBlockReachDistance()); if (rot.isPresent()) { return Optional.of(new Tuple<>(pos, rot.get())); } } } } } return Optional.empty(); } public static class Placement { private final int hotbarSelection; private final BlockPos placeAgainst; private final Direction side; private final Rotation rot; public Placement(int hotbarSelection, BlockPos placeAgainst, Direction side, Rotation rot) { this.hotbarSelection = hotbarSelection; this.placeAgainst = placeAgainst; this.side = side; this.rot = rot; } } private Optional<Placement> searchForPlacables(BuilderCalculationContext bcc, List<BlockState> desirableOnHotbar) { 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; BlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired == null) { continue; // irrelevant } BlockState curr = bcc.bsi.get0(x, y, z); if (MovementHelper.isReplaceable(x, y, z, curr, bcc.bsi) && !valid(curr, desired, false)) { if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() instanceof AirBlock) { continue; } desirableOnHotbar.add(desired); Optional<Placement> opt = possibleToPlace(desired, x, y, z, bcc.bsi); if (opt.isPresent()) { return opt; } } } } } return Optional.empty(); } public boolean placementPlausible(BlockPos pos, BlockState state) { VoxelShape voxelshape = state.getCollisionShape(ctx.world(), pos); return voxelshape.isEmpty() || ctx.world().isUnobstructed(null, voxelshape.move(pos.getX(), pos.getY(), pos.getZ())); } private Optional<Placement> possibleToPlace(BlockState toPlace, int x, int y, int z, BlockStateInterface bsi) { for (Direction against : Direction.values()) { BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).relative(against); BlockState placeAgainstState = bsi.get0(placeAgainstPos); if (MovementHelper.isReplaceable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { continue; } if (!toPlace.canSurvive(ctx.world(), new BetterBlockPos(x, y, z))) { continue; } if (!placementPlausible(new BetterBlockPos(x, y, z), toPlace)) { continue; } VoxelShape shape = placeAgainstState.getShape(ctx.world(), placeAgainstPos); if (shape.isEmpty()) { continue; } AABB aabb = shape.bounds(); for (Vec3 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(RayTraceUtils.inferSneakingEyePosition(ctx.player()), new Vec3(placeX, placeY, placeZ), ctx.playerRotations()); Rotation actualRot = baritone.getLookBehavior().getAimProcessor().peekRotation(rot); HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), actualRot, ctx.playerController().getBlockReachDistance(), true); if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(placeAgainstPos) && ((BlockHitResult) result).getDirection() == against.getOpposite()) { OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, actualRot); if (hotbar.isPresent()) { return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); } } } } return Optional.empty(); } private OptionalInt hasAnyItemThatWouldPlace(BlockState desired, HitResult result, Rotation rot) { for (int i = 0; i < 9; i++) { ItemStack stack = ctx.player().getInventory().items.get(i); if (stack.isEmpty() || !(stack.getItem() instanceof BlockItem)) { continue; } float originalYaw = ctx.player().getYRot(); float originalPitch = ctx.player().getXRot(); // the state depends on the facing of the player sometimes ctx.player().setYRot(rot.getYaw()); ctx.player().setXRot(rot.getPitch()); BlockPlaceContext meme = new BlockPlaceContext(new UseOnContext( ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, (BlockHitResult) result ) {}); // that {} gives us access to a protected constructor lmfao BlockState wouldBePlaced = ((BlockItem) stack.getItem()).getBlock().getStateForPlacement(meme); ctx.player().setYRot(originalYaw); ctx.player().setXRot(originalPitch); if (wouldBePlaced == null) { continue; } if (!meme.canPlace()) { continue; } if (valid(wouldBePlaced, desired, true)) { return OptionalInt.of(i); } } return OptionalInt.empty(); } private static Vec3[] aabbSideMultipliers(Direction side) { switch (side) { case UP: return new Vec3[]{new Vec3(0.5, 1, 0.5), new Vec3(0.1, 1, 0.5), new Vec3(0.9, 1, 0.5), new Vec3(0.5, 1, 0.1), new Vec3(0.5, 1, 0.9)}; case DOWN: return new Vec3[]{new Vec3(0.5, 0, 0.5), new Vec3(0.1, 0, 0.5), new Vec3(0.9, 0, 0.5), new Vec3(0.5, 0, 0.1), new Vec3(0.5, 0, 0.9)}; case NORTH: case SOUTH: case EAST: case WEST: double x = side.getStepX() == 0 ? 0.5 : (1 + side.getStepX()) / 2D; double z = side.getStepZ() == 0 ? 0.5 : (1 + side.getStepZ()) / 2D; return new Vec3[]{new Vec3(x, 0.25, z), new Vec3(x, 0.75, z)}; default: // null throw new IllegalStateException("Unexpected side " + side); } } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { return onTick(calcFailed, isSafeToCancel, 0); } private PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel, int recursions) { if (recursions > 100) { // onTick calls itself, don't crash return new PathingCommand(null, PathingCommandType.SET_GOAL_AND_PATH); } approxPlaceable = approxPlaceable(36); if (baritone.getInputOverrideHandler().isInputForcedDown(Input.CLICK_LEFT)) { ticks = 5; } else { ticks--; } baritone.getInputOverrideHandler().clearAllKeys(); if (paused) { return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } if (Baritone.settings().buildInLayers.value) { if (realSchematic == null) { realSchematic = schematic; } ISchematic realSchematic = this.realSchematic; // wrap this properly, dont just have the inner class refer to the builderprocess.this int minYInclusive; int maxYInclusive; // layer = 0 should be nothing // layer = realSchematic.heightY() should be everything if (Baritone.settings().layerOrder.value) { // top to bottom maxYInclusive = realSchematic.heightY() - 1; minYInclusive = realSchematic.heightY() - layer * Baritone.settings().layerHeight.value; } else { maxYInclusive = layer * Baritone.settings().layerHeight.value - 1; minYInclusive = 0; } schematic = new ISchematic() { @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { return realSchematic.desiredState(x, y, z, current, BuilderProcess.this.approxPlaceable); } @Override public boolean inSchematic(int x, int y, int z, BlockState currentState) { return ISchematic.super.inSchematic(x, y, z, currentState) && y >= minYInclusive && y <= maxYInclusive && realSchematic.inSchematic(x, y, z, currentState); } @Override public void reset() { realSchematic.reset(); } @Override public int widthX() { return realSchematic.widthX(); } @Override public int heightY() { return realSchematic.heightY(); } @Override public int lengthZ() { return realSchematic.lengthZ(); } }; } BuilderCalculationContext bcc = new BuilderCalculationContext(); if (!recalc(bcc)) { if (Baritone.settings().buildInLayers.value && layer * Baritone.settings().layerHeight.value < stopAtHeight) { logDirect("Starting layer " + layer); layer++; return onTick(calcFailed, isSafeToCancel, recursions + 1); } Vec3i repeat = Baritone.settings().buildRepeat.value; int max = Baritone.settings().buildRepeatCount.value; numRepeats++; if (repeat.equals(new Vec3i(0, 0, 0)) || (max != -1 && numRepeats >= max)) { logDirect("Done building"); if (Baritone.settings().notificationOnBuildFinished.value) { logNotification("Done building", false); } onLostControl(); return null; } // build repeat time layer = 0; origin = new BlockPos(origin).offset(repeat); if (!Baritone.settings().buildRepeatSneaky.value) { schematic.reset(); } logDirect("Repeating build in vector " + repeat + ", new origin is " + origin); return onTick(calcFailed, isSafeToCancel, recursions + 1); } if (Baritone.settings().distanceTrim.value) { trim(); } Optional<Tuple<BetterBlockPos, Rotation>> toBreak = toBreakNearPlayer(bcc); if (toBreak.isPresent() && isSafeToCancel && ctx.player().isOnGround()) { // we'd like to pause to break this block // only change look direction if it's safe (don't want to fuck up an in progress parkour for example Rotation rot = toBreak.get().getB(); BetterBlockPos pos = toBreak.get().getA(); baritone.getLookBehavior().updateTarget(rot, true); MovementHelper.switchToBestToolFor(ctx, bcc.get(pos)); if (ctx.player().isCrouching()) { // really horrible bug where a block is visible for breaking while sneaking but not otherwise // so you can't see it, it goes to place something else, sneaks, then the next tick it tries to break // and is unable since it's unsneaked in the intermediary tick baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); } if (ctx.isLookingAt(pos) || ctx.playerRotations().isReallyCloseTo(rot)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } List<BlockState> desirableOnHotbar = new ArrayList<>(); Optional<Placement> toPlace = searchForPlacables(bcc, desirableOnHotbar); if (toPlace.isPresent() && isSafeToCancel && ctx.player().isOnGround() && ticks <= 0) { Rotation rot = toPlace.get().rot; baritone.getLookBehavior().updateTarget(rot, true); ctx.player().getInventory().selected = toPlace.get().hotbarSelection; baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); if ((ctx.isLookingAt(toPlace.get().placeAgainst) && ((BlockHitResult) ctx.objectMouseOver()).getDirection().equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } if (Baritone.settings().allowInventory.value) { ArrayList<Integer> usefulSlots = new ArrayList<>(); List<BlockState> noValidHotbarOption = new ArrayList<>(); outer: for (BlockState desired : desirableOnHotbar) { for (int i = 0; i < 9; i++) { if (valid(approxPlaceable.get(i), desired, true)) { usefulSlots.add(i); continue outer; } } noValidHotbarOption.add(desired); } outer: for (int i = 9; i < 36; i++) { for (BlockState desired : noValidHotbarOption) { if (valid(approxPlaceable.get(i), desired, true)) { if (!baritone.getInventoryBehavior().attemptToPutOnHotbar(i, usefulSlots::contains)) { // awaiting inventory move, so pause return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } break outer; } } } } Goal goal = assemble(bcc, approxPlaceable.subList(0, 9)); if (goal == null) { goal = assemble(bcc, approxPlaceable, true); // we're far away, so assume that we have our whole inventory to recalculate placeable properly if (goal == null) { if (Baritone.settings().skipFailedLayers.value && Baritone.settings().buildInLayers.value && layer * Baritone.settings().layerHeight.value < realSchematic.heightY()) { logDirect("Skipping layer that I cannot construct! Layer #" + layer); layer++; return onTick(calcFailed, isSafeToCancel, recursions + 1); } logDirect("Unable to do it. Pausing. resume to resume, cancel to cancel"); paused = true; return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } return new PathingCommandContext(goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH, bcc); } private boolean recalc(BuilderCalculationContext bcc) { if (incorrectPositions == null) { incorrectPositions = new HashSet<>(); fullRecalc(bcc); if (incorrectPositions.isEmpty()) { return false; } } recalcNearby(bcc); if (incorrectPositions.isEmpty()) { fullRecalc(bcc); } return !incorrectPositions.isEmpty(); } private void trim() { HashSet<BetterBlockPos> copy = new HashSet<>(incorrectPositions); copy.removeIf(pos -> pos.distSqr(ctx.player().blockPosition()) > 200); if (!copy.isEmpty()) { incorrectPositions = copy; } } private void recalcNearby(BuilderCalculationContext bcc) { BetterBlockPos center = ctx.playerFeet(); int radius = Baritone.settings().builderTickScanRadius.value; for (int dx = -radius; dx <= radius; dx++) { for (int dy = -radius; dy <= radius; dy++) { for (int dz = -radius; dz <= radius; dz++) { int x = center.x + dx; int y = center.y + dy; int z = center.z + dz; BlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired != null) { // we care about this position BetterBlockPos pos = new BetterBlockPos(x, y, z); if (valid(bcc.bsi.get0(x, y, z), desired, false)) { incorrectPositions.remove(pos); observedCompleted.add(BetterBlockPos.longHash(pos)); } else { incorrectPositions.add(pos); observedCompleted.remove(BetterBlockPos.longHash(pos)); } } } } } } private void fullRecalc(BuilderCalculationContext bcc) { incorrectPositions = new HashSet<>(); for (int y = 0; y < schematic.heightY(); y++) { for (int z = 0; z < schematic.lengthZ(); z++) { for (int x = 0; x < schematic.widthX(); x++) { int blockX = x + origin.getX(); int blockY = y + origin.getY(); int blockZ = z + origin.getZ(); BlockState current = bcc.bsi.get0(blockX, blockY, blockZ); if (!schematic.inSchematic(x, y, z, current)) { continue; } if (bcc.bsi.worldContainsLoadedChunk(blockX, blockZ)) { // check if its in render distance, not if its in cache // we can directly observe this block, it is in render distance if (valid(bcc.bsi.get0(blockX, blockY, blockZ), schematic.desiredState(x, y, z, current, this.approxPlaceable), false)) { observedCompleted.add(BetterBlockPos.longHash(blockX, blockY, blockZ)); } else { incorrectPositions.add(new BetterBlockPos(blockX, blockY, blockZ)); observedCompleted.remove(BetterBlockPos.longHash(blockX, blockY, blockZ)); if (incorrectPositions.size() > Baritone.settings().incorrectSize.value) { return; } } continue; } // this is not in render distance if (!observedCompleted.contains(BetterBlockPos.longHash(blockX, blockY, blockZ))) { // and we've never seen this position be correct // therefore mark as incorrect incorrectPositions.add(new BetterBlockPos(blockX, blockY, blockZ)); if (incorrectPositions.size() > Baritone.settings().incorrectSize.value) { return; } } } } } } private Goal assemble(BuilderCalculationContext bcc, List<BlockState> approxPlaceable) { return assemble(bcc, approxPlaceable, false); } private Goal assemble(BuilderCalculationContext bcc, List<BlockState> approxPlaceable, boolean logMissing) { List<BetterBlockPos> placeable = new ArrayList<>(); List<BetterBlockPos> breakable = new ArrayList<>(); List<BetterBlockPos> sourceLiquids = new ArrayList<>(); List<BetterBlockPos> flowingLiquids = new ArrayList<>(); Map<BlockState, Integer> missing = new HashMap<>(); List<BetterBlockPos> outOfBounds = new ArrayList<>(); incorrectPositions.forEach(pos -> { BlockState state = bcc.bsi.get0(pos); if (state.getBlock() instanceof AirBlock) { BlockState desired = bcc.getSchematic(pos.x, pos.y, pos.z, state); if (desired == null) { outOfBounds.add(pos); } else if (containsBlockState(approxPlaceable, desired)) { placeable.add(pos); } else { missing.put(desired, 1 + missing.getOrDefault(desired, 0)); } } else { if (state.getBlock() instanceof LiquidBlock) { // if the block itself is JUST a liquid (i.e. not just a waterlogged block), we CANNOT break it // TODO for 1.13 make sure that this only matches pure water, not waterlogged blocks if (!MovementHelper.possiblyFlowing(state)) { // if it's a source block then we want to replace it with a throwaway sourceLiquids.add(pos); } else { flowingLiquids.add(pos); } } else { breakable.add(pos); } } }); incorrectPositions.removeAll(outOfBounds); List<Goal> toBreak = new ArrayList<>(); breakable.forEach(pos -> toBreak.add(breakGoal(pos, bcc))); List<Goal> toPlace = new ArrayList<>(); placeable.forEach(pos -> { if (!placeable.contains(pos.below()) && !placeable.contains(pos.below(2))) { toPlace.add(placementGoal(pos, bcc)); } }); sourceLiquids.forEach(pos -> toPlace.add(new GoalBlock(pos.above()))); if (!toPlace.isEmpty()) { return new JankyGoalComposite(new GoalComposite(toPlace.toArray(new Goal[0])), new GoalComposite(toBreak.toArray(new Goal[0]))); } if (toBreak.isEmpty()) { if (logMissing && !missing.isEmpty()) { logDirect("Missing materials for at least:"); logDirect(missing.entrySet().stream() .map(e -> String.format("%sx %s", e.getValue(), e.getKey())) .collect(Collectors.joining("\n"))); } if (logMissing && !flowingLiquids.isEmpty()) { logDirect("Unreplaceable liquids at at least:"); logDirect(flowingLiquids.stream() .map(p -> String.format("%s %s %s", p.x, p.y, p.z)) .collect(Collectors.joining("\n"))); } return null; } return new GoalComposite(toBreak.toArray(new Goal[0])); } public static class JankyGoalComposite implements Goal { private final Goal primary; private final Goal fallback; public JankyGoalComposite(Goal primary, Goal fallback) { this.primary = primary; this.fallback = fallback; } @Override public boolean isInGoal(int x, int y, int z) { return primary.isInGoal(x, y, z) || fallback.isInGoal(x, y, z); } @Override public double heuristic(int x, int y, int z) { return primary.heuristic(x, y, z); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JankyGoalComposite goal = (JankyGoalComposite) o; return Objects.equals(primary, goal.primary) && Objects.equals(fallback, goal.fallback); } @Override public int hashCode() { int hash = -1701079641; hash = hash * 1196141026 + primary.hashCode(); hash = hash * -80327868 + fallback.hashCode(); return hash; } @Override public String toString() { return "JankyComposite Primary: " + primary + " Fallback: " + fallback; } } public static class GoalBreak extends GoalGetToBlock { public GoalBreak(BlockPos pos) { super(pos); } @Override public boolean isInGoal(int x, int y, int z) { // can't stand right on top of a block, that might not work (what if it's unsupported, can't break then) if (y > this.y) { return false; } // but any other adjacent works for breaking, including inside or below return super.isInGoal(x, y, z); } @Override public String toString() { return String.format( "GoalBreak{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } @Override public int hashCode() { return super.hashCode() * 1636324008; } } private Goal placementGoal(BlockPos pos, BuilderCalculationContext bcc) { if (!(ctx.world().getBlockState(pos).getBlock() instanceof AirBlock)) { // TODO can this even happen? return new GoalPlace(pos); } boolean allowSameLevel = !(ctx.world().getBlockState(pos.above()).getBlock() instanceof AirBlock); BlockState current = ctx.world().getBlockState(pos); for (Direction facing : Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP) { //noinspection ConstantConditions if (MovementHelper.canPlaceAgainst(ctx, pos.relative(facing)) && placementPlausible(pos, bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ(), current))) { return new GoalAdjacent(pos, pos.relative(facing), allowSameLevel); } } return new GoalPlace(pos); } private Goal breakGoal(BlockPos pos, BuilderCalculationContext bcc) { if (Baritone.settings().goalBreakFromAbove.value && bcc.bsi.get0(pos.above()).getBlock() instanceof AirBlock && bcc.bsi.get0(pos.above(2)).getBlock() instanceof AirBlock) { // TODO maybe possible without the up(2) check? return new JankyGoalComposite(new GoalBreak(pos), new GoalGetToBlock(pos.above()) { @Override public boolean isInGoal(int x, int y, int z) { if (y > this.y || (x == this.x && y == this.y && z == this.z)) { return false; } return super.isInGoal(x, y, z); } }); } return new GoalBreak(pos); } public static class GoalAdjacent extends GoalGetToBlock { private boolean allowSameLevel; private BlockPos no; public GoalAdjacent(BlockPos pos, BlockPos no, boolean allowSameLevel) { super(pos); this.no = no; this.allowSameLevel = allowSameLevel; } @Override public boolean isInGoal(int x, int y, int z) { if (x == this.x && y == this.y && z == this.z) { return false; } if (x == no.getX() && y == no.getY() && z == no.getZ()) { return false; } if (!allowSameLevel && y == this.y - 1) { return false; } if (y < this.y - 1) { return false; } return super.isInGoal(x, y, z); } @Override public double heuristic(int x, int y, int z) { // prioritize lower y coordinates return this.y * 100 + super.heuristic(x, y, z); } @Override public boolean equals(Object o) { if (!super.equals(o)) { return false; } GoalAdjacent goal = (GoalAdjacent) o; return allowSameLevel == goal.allowSameLevel && Objects.equals(no, goal.no); } @Override public int hashCode() { int hash = 806368046; hash = hash * 1412661222 + super.hashCode(); hash = hash * 1730799370 + (int) BetterBlockPos.longHash(no.getX(), no.getY(), no.getZ()); hash = hash * 260592149 + (allowSameLevel ? -1314802005 : 1565710265); return hash; } @Override public String toString() { return String.format( "GoalAdjacent{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } } public static class GoalPlace extends GoalBlock { public GoalPlace(BlockPos placeAt) { super(placeAt.above()); } @Override public double heuristic(int x, int y, int z) { // prioritize lower y coordinates return this.y * 100 + super.heuristic(x, y, z); } @Override public int hashCode() { return super.hashCode() * 1910811835; } @Override public String toString() { return String.format( "GoalPlace{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } } @Override public void onLostControl() { incorrectPositions = null; name = null; schematic = null; realSchematic = null; layer = Baritone.settings().startAtLayer.value; numRepeats = 0; paused = false; observedCompleted = null; } @Override public String displayName0() { return paused ? "Builder Paused" : "Building " + name; } @Override public Optional<Integer> getMinLayer() { if (Baritone.settings().buildInLayers.value) { return Optional.of(this.layer); } return Optional.empty(); } @Override public Optional<Integer> getMaxLayer() { if (Baritone.settings().buildInLayers.value) { return Optional.of(this.stopAtHeight); } return Optional.empty(); } private List<BlockState> approxPlaceable(int size) { List<BlockState> result = new ArrayList<>(); for (int i = 0; i < size; i++) { ItemStack stack = ctx.player().getInventory().items.get(i); if (stack.isEmpty() || !(stack.getItem() instanceof BlockItem)) { result.add(Blocks.AIR.defaultBlockState()); continue; } // <toxic cloud> BlockState itemState = ((BlockItem) stack.getItem()) .getBlock() .getStateForPlacement( new BlockPlaceContext( new UseOnContext(ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, new BlockHitResult(new Vec3(ctx.player().position().x, ctx.player().position().y, ctx.player().position().z), Direction.UP, ctx.playerFeet(), false)) {} ) ); if (itemState != null) { result.add(itemState); } else { result.add(Blocks.AIR.defaultBlockState()); } // </toxic cloud> } return result; } private static boolean sameBlockstate(BlockState first, BlockState second) { if (first.getBlock() != second.getBlock()) { return false; } boolean ignoreDirection = Baritone.settings().buildIgnoreDirection.value; List<String> ignoredProps = Baritone.settings().buildIgnoreProperties.value; if (!ignoreDirection && ignoredProps.isEmpty()) { return first.equals(second); // early return if no properties are being ignored } ImmutableMap<Property<?>, Comparable<?>> map1 = first.getValues(); ImmutableMap<Property<?>, Comparable<?>> map2 = second.getValues(); for (Property<?> prop : map1.keySet()) { if (map1.get(prop) != map2.get(prop) && !(ignoreDirection && ORIENTATION_PROPS.contains(prop)) && !ignoredProps.contains(prop.getName())) { return false; } } return true; } private static boolean containsBlockState(Collection<BlockState> states, BlockState state) { for (BlockState testee : states) { if (sameBlockstate(testee, state)) { return true; } } return false; } private static boolean valid(BlockState current, BlockState desired, boolean itemVerify) { if (desired == null) { return true; } if (current.getBlock() instanceof LiquidBlock && Baritone.settings().okIfWater.value) { return true; } if (current.getBlock() instanceof AirBlock && desired.getBlock() instanceof AirBlock) { return true; } if (current.getBlock() instanceof AirBlock && Baritone.settings().okIfAir.value.contains(desired.getBlock())) { return true; } if (desired.getBlock() instanceof AirBlock && Baritone.settings().buildIgnoreBlocks.value.contains(current.getBlock())) { return true; } if (!(current.getBlock() instanceof AirBlock) && Baritone.settings().buildIgnoreExisting.value && !itemVerify) { return true; } if (Baritone.settings().buildValidSubstitutes.value.getOrDefault(desired.getBlock(), Collections.emptyList()).contains(current.getBlock()) && !itemVerify) { return true; } if (current.equals(desired)) { return true; } return sameBlockstate(current, desired); } public class BuilderCalculationContext extends CalculationContext { private final List<BlockState> placeable; private final ISchematic schematic; private final int originX; private final int originY; private final int originZ; public BuilderCalculationContext() { super(BuilderProcess.this.baritone, true); // wew lad this.placeable = approxPlaceable(9); this.schematic = BuilderProcess.this.schematic; this.originX = origin.getX(); this.originY = origin.getY(); this.originZ = origin.getZ(); this.jumpPenalty += 10; this.backtrackCostFavoringCoefficient = 1; } private BlockState getSchematic(int x, int y, int z, BlockState current) { if (schematic.inSchematic(x - originX, y - originY, z - originZ, current)) { return schematic.desiredState(x - originX, y - originY, z - originZ, current, BuilderProcess.this.approxPlaceable); } else { return null; } } @Override public double costOfPlacingAt(int x, int y, int z, BlockState current) { if (isPossiblyProtected(x, y, z) || !worldBorder.canPlaceAt(x, z)) { // make calculation fail properly if we can't build return COST_INF; } BlockState sch = getSchematic(x, y, z, current); if (sch != null) { // TODO this can return true even when allowPlace is off.... is that an issue? if (sch.getBlock() instanceof AirBlock) { // we want this to be air, but they're asking if they can place here // this won't be a schematic block, this will be a throwaway return placeBlockCost * Baritone.settings().placeIncorrectBlockPenaltyMultiplier.value; // we're going to have to break it eventually } if (placeable.contains(sch)) { return 0; // thats right we gonna make it FREE to place a block where it should go in a structure // no place block penalty at all 😎 // i'm such an idiot that i just tried to copy and paste the epic gamer moment emoji too // get added to unicode when? } if (!hasThrowaway) { return COST_INF; } // we want it to be something that we don't have // even more of a pain to place something wrong return placeBlockCost * 1.5 * Baritone.settings().placeIncorrectBlockPenaltyMultiplier.value; } else { if (hasThrowaway) { return placeBlockCost; } else { return COST_INF; } } } @Override public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { if ((!allowBreak && !allowBreakAnyway.contains(current.getBlock())) || isPossiblyProtected(x, y, z)) { return COST_INF; } BlockState sch = getSchematic(x, y, z, current); if (sch != null) { if (sch.getBlock() instanceof AirBlock) { // it should be air // regardless of current contents, we can break it return 1; } // it should be a real block // is it already that block? if (valid(bsi.get0(x, y, z), sch, false)) { return Baritone.settings().breakCorrectBlockPenaltyMultiplier.value; } else { // can break if it's wrong // would be great to return less than 1 here, but that would actually make the cost calculation messed up // since we're breaking a block, if we underestimate the cost, then it'll fail when it really takes the correct amount of time return 1; } // TODO do blocks in render distace only? // TODO allow breaking blocks that we have a tool to harvest and immediately place back? } else { return 1; // why not lol } } } } ================================================ FILE: src/main/java/baritone/process/CustomGoalProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.pathing.goals.Goal; import baritone.api.process.ICustomGoalProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.utils.BaritoneProcessHelper; /** * As set by ExampleBaritoneControl or something idk * * @author leijurv */ public final class CustomGoalProcess extends BaritoneProcessHelper implements ICustomGoalProcess { /** * The current goal */ private Goal goal; /** * The most recent goal. Not invalidated upon {@link #onLostControl()} */ private Goal mostRecentGoal; /** * The current process state. * * @see State */ private State state; public CustomGoalProcess(Baritone baritone) { super(baritone); } @Override public void setGoal(Goal goal) { this.goal = goal; this.mostRecentGoal = goal; if (baritone.getElytraProcess().isActive()) { baritone.getElytraProcess().pathTo(goal); } if (this.state == State.NONE) { this.state = State.GOAL_SET; } if (this.state == State.EXECUTING) { this.state = State.PATH_REQUESTED; } } @Override public void path() { this.state = State.PATH_REQUESTED; } @Override public Goal getGoal() { return this.goal; } @Override public Goal mostRecentGoal() { return this.mostRecentGoal; } @Override public boolean isActive() { return this.state != State.NONE; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { switch (this.state) { case GOAL_SET: return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); case PATH_REQUESTED: // return FORCE_REVALIDATE_GOAL_AND_PATH just once PathingCommand ret = new PathingCommand(this.goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); this.state = State.EXECUTING; return ret; case EXECUTING: if (calcFailed) { onLostControl(); return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); } if (this.goal == null || (this.goal.isInGoal(ctx.playerFeet()) && this.goal.isInGoal(baritone.getPathingBehavior().pathStart()))) { onLostControl(); // we're there xd if (Baritone.settings().disconnectOnArrival.value) { ctx.world().disconnect(); } if (Baritone.settings().notificationOnPathComplete.value) { logNotification("Pathing complete", false); } return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); } return new PathingCommand(this.goal, PathingCommandType.SET_GOAL_AND_PATH); default: throw new IllegalStateException("Unexpected state " + this.state); } } @Override public void onLostControl() { this.state = State.NONE; this.goal = null; } @Override public String displayName0() { return "Custom Goal " + this.goal; } protected enum State { NONE, GOAL_SET, PATH_REQUESTED, EXECUTING } } ================================================ FILE: src/main/java/baritone/process/ElytraProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.event.events.*; import baritone.api.event.events.type.EventState; import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.pathing.goals.GoalXZ; import baritone.api.pathing.goals.GoalYLevel; import baritone.api.pathing.movement.IMovement; import baritone.api.pathing.path.IPathExecutor; import baritone.api.process.IBaritoneProcess; import baritone.api.process.IElytraProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.movements.MovementFall; import baritone.process.elytra.ElytraBehavior; import baritone.process.elytra.NetherPathfinderContext; import baritone.process.elytra.NullElytraProcess; import baritone.utils.BaritoneProcessHelper; import baritone.utils.PathingCommandContext; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import java.util.*; import static baritone.api.pathing.movement.ActionCosts.COST_INF; public class ElytraProcess extends BaritoneProcessHelper implements IBaritoneProcess, IElytraProcess, AbstractGameEventListener { public State state; private boolean goingToLandingSpot; private BetterBlockPos landingSpot; private boolean reachedGoal; // this basically just prevents potential notification spam private Goal goal; private ElytraBehavior behavior; private boolean predictingTerrain; @Override public void onLostControl() { this.state = State.START_FLYING; // TODO: null state? this.goingToLandingSpot = false; this.landingSpot = null; this.reachedGoal = false; this.goal = null; destroyBehaviorAsync(); } private ElytraProcess(Baritone baritone) { super(baritone); baritone.getGameEventHandler().registerEventListener(this); } public static IElytraProcess create(final Baritone baritone) { return NetherPathfinderContext.isSupported() ? new ElytraProcess(baritone) : new NullElytraProcess(baritone); } @Override public boolean isActive() { return this.behavior != null; } @Override public void resetState() { BlockPos destination = this.currentDestination(); this.onLostControl(); if (destination != null) { this.pathTo(destination); this.repackChunks(); } } private static final String AUTO_JUMP_FAILURE_MSG = "Failed to compute a walking path to a spot to jump off from. Consider starting from a higher location, near an overhang. Or, you can disable elytraAutoJump and just manually begin gliding."; @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { final long seedSetting = Baritone.settings().elytraNetherSeed.value; if (seedSetting != this.behavior.context.getSeed()) { logDirect("Nether seed changed, recalculating path"); this.resetState(); } if (predictingTerrain != Baritone.settings().elytraPredictTerrain.value) { logDirect("elytraPredictTerrain setting changed, recalculating path"); predictingTerrain = Baritone.settings().elytraPredictTerrain.value; this.resetState(); } this.behavior.onTick(); if (calcFailed) { onLostControl(); logDirect(AUTO_JUMP_FAILURE_MSG); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } boolean safetyLanding = false; if (ctx.player().isFallFlying() && shouldLandForSafety()) { if (Baritone.settings().elytraAllowEmergencyLand.value) { logDirect("Emergency landing - almost out of elytra durability or fireworks"); safetyLanding = true; } else { logDirect("almost out of elytra durability or fireworks, but I'm going to continue since elytraAllowEmergencyLand is false"); } } if (ctx.player().isFallFlying() && this.state != State.LANDING && (this.behavior.pathManager.isComplete() || safetyLanding)) { final BetterBlockPos last = this.behavior.pathManager.path.getLast(); if (last != null && (ctx.player().position().distanceToSqr(last.getCenter()) < (48 * 48) || safetyLanding) && (!goingToLandingSpot || (safetyLanding && this.landingSpot == null))) { logDirect("Path complete, picking a nearby safe landing spot..."); BetterBlockPos landingSpot = findSafeLandingSpot(ctx.playerFeet()); // if this fails we will just keep orbiting the last node until we run out of rockets or the user intervenes if (landingSpot != null) { this.pathTo0(landingSpot, true); this.landingSpot = landingSpot; } this.goingToLandingSpot = true; } if (last != null && ctx.player().position().distanceToSqr(last.getCenter()) < 1) { if (Baritone.settings().notificationOnPathComplete.value && !reachedGoal) { logNotification("Pathing complete", false); } if (Baritone.settings().disconnectOnArrival.value && !reachedGoal) { // don't be active when the user logs back in this.onLostControl(); ctx.world().disconnect(); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } reachedGoal = true; // we are goingToLandingSpot and we are in the last node of the path if (this.goingToLandingSpot) { this.state = State.LANDING; logDirect("Above the landing spot, landing..."); } } } if (this.state == State.LANDING) { final BetterBlockPos endPos = this.landingSpot != null ? this.landingSpot : behavior.pathManager.path.getLast(); if (ctx.player().isFallFlying() && endPos != null) { Vec3 from = ctx.player().position(); Vec3 to = new Vec3(((double) endPos.x) + 0.5, from.y, ((double) endPos.z) + 0.5); Rotation rotation = RotationUtils.calcRotationFromVec3d(from, to, ctx.playerRotations()); baritone.getLookBehavior().updateTarget(new Rotation(rotation.getYaw(), 0), false); // this will be overwritten, probably, by behavior tick if (ctx.player().position().y < endPos.y - LANDING_COLUMN_HEIGHT) { logDirect("bad landing spot, trying again..."); landingSpotIsBad(endPos); } } } if (ctx.player().isFallFlying()) { behavior.landingMode = this.state == State.LANDING; this.goal = null; baritone.getInputOverrideHandler().clearAllKeys(); behavior.tick(); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } else if (this.state == State.LANDING) { if (ctx.playerMotion().multiply(1, 0, 1).length() > 0.001) { logDirect("Landed, but still moving, waiting for velocity to die down... "); baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } logDirect("Done :)"); baritone.getInputOverrideHandler().clearAllKeys(); this.onLostControl(); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } if (this.state == State.FLYING || this.state == State.START_FLYING) { this.state = ctx.player().isOnGround() && Baritone.settings().elytraAutoJump.value ? State.LOCATE_JUMP : State.START_FLYING; } if (this.state == State.LOCATE_JUMP) { if (shouldLandForSafety()) { logDirect("Not taking off, because elytra durability or fireworks are so low that I would immediately emergency land anyway."); onLostControl(); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } if (this.goal == null) { this.goal = new GoalYLevel(31); } final IPathExecutor executor = baritone.getPathingBehavior().getCurrent(); if (executor != null && executor.getPath().getGoal() == this.goal) { final IMovement fall = executor.getPath().movements().stream() .filter(movement -> movement instanceof MovementFall) .findFirst().orElse(null); if (fall != null) { final BetterBlockPos from = new BetterBlockPos( (fall.getSrc().x + fall.getDest().x) / 2, (fall.getSrc().y + fall.getDest().y) / 2, (fall.getSrc().z + fall.getDest().z) / 2 ); behavior.pathManager.pathToDestination(from).whenComplete((result, ex) -> { if (ex == null) { this.state = State.GET_TO_JUMP; return; } onLostControl(); }); this.state = State.PAUSE; } else { onLostControl(); logDirect(AUTO_JUMP_FAILURE_MSG); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } } return new PathingCommandContext(this.goal, PathingCommandType.SET_GOAL_AND_PAUSE, new WalkOffCalculationContext(baritone)); } // yucky if (this.state == State.PAUSE) { return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } if (this.state == State.GET_TO_JUMP) { final IPathExecutor executor = baritone.getPathingBehavior().getCurrent(); final boolean canStartFlying = ctx.player().fallDistance > 1.0f && !isSafeToCancel && executor != null && executor.getPath().movements().get(executor.getPosition()) instanceof MovementFall; if (canStartFlying) { this.state = State.START_FLYING; } else { return new PathingCommand(null, PathingCommandType.SET_GOAL_AND_PATH); } } if (this.state == State.START_FLYING) { if (!isSafeToCancel) { // owned baritone.getPathingBehavior().secretInternalSegmentCancel(); } baritone.getInputOverrideHandler().clearAllKeys(); if (ctx.player().fallDistance > 1.0f) { baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); } } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } public void landingSpotIsBad(BetterBlockPos endPos) { badLandingSpots.add(endPos); goingToLandingSpot = false; this.landingSpot = null; this.state = State.FLYING; } private void destroyBehaviorAsync() { ElytraBehavior behavior = this.behavior; if (behavior != null) { this.behavior = null; Baritone.getExecutor().execute(behavior::destroy); } } @Override public double priority() { return 0; // higher priority than CustomGoalProcess } @Override public String displayName0() { return "Elytra - " + this.state.description; } @Override public void repackChunks() { if (this.behavior != null) { this.behavior.repackChunks(); } } @Override public BlockPos currentDestination() { return this.behavior != null ? this.behavior.destination : null; } @Override public List<BetterBlockPos> getPath() { return this.behavior != null ? behavior.pathManager.getPath() : Collections.emptyList(); } @Override public void pathTo(BlockPos destination) { this.pathTo0(destination, false); } private void pathTo0(BlockPos destination, boolean appendDestination) { if (ctx.player() == null || ctx.player().level.dimension() != Level.NETHER) { return; } this.onLostControl(); this.predictingTerrain = Baritone.settings().elytraPredictTerrain.value; this.behavior = new ElytraBehavior(this.baritone, this, destination, appendDestination); if (ctx.world() != null) { this.behavior.repackChunks(); } this.behavior.pathTo(); } @Override public void pathTo(Goal iGoal) { final int x; final int y; final int z; if (iGoal instanceof GoalXZ) { GoalXZ goal = (GoalXZ) iGoal; x = goal.getX(); y = 64; z = goal.getZ(); } else if (iGoal instanceof GoalBlock) { GoalBlock goal = (GoalBlock) iGoal; x = goal.x; y = goal.y; z = goal.z; } else { throw new IllegalArgumentException("The goal must be a GoalXZ or GoalBlock"); } if (y <= 0 || y >= 128) { throw new IllegalArgumentException("The y of the goal is not between 0 and 128"); } this.pathTo(new BlockPos(x, y, z)); } private boolean shouldLandForSafety() { ItemStack chest = ctx.player().getItemBySlot(EquipmentSlot.CHEST); if (chest.getItem() != Items.ELYTRA || chest.getItem().getMaxDamage() - chest.getDamageValue() < Baritone.settings().elytraMinimumDurability.value) { // elytrabehavior replaces when durability <= minimumDurability, so if durability < minimumDurability then we can reasonably assume that the elytra will soon be broken without replacement return true; } NonNullList<ItemStack> inv = ctx.player().getInventory().items; int qty = 0; for (int i = 0; i < 36; i++) { if (ElytraBehavior.isFireworks(inv.get(i))) { qty += inv.get(i).getCount(); } } if (qty <= Baritone.settings().elytraMinFireworksBeforeLanding.value) { return true; } return false; } @Override public boolean isLoaded() { return true; } @Override public boolean isSafeToCancel() { return !this.isActive() || !(this.state == State.FLYING || this.state == State.START_FLYING); } public enum State { LOCATE_JUMP("Finding spot to jump off"), PAUSE("Waiting for elytra path"), GET_TO_JUMP("Walking to takeoff"), START_FLYING("Begin flying"), FLYING("Flying"), LANDING("Landing"); public final String description; State(String desc) { this.description = desc; } } @Override public void onRenderPass(RenderEvent event) { if (this.behavior != null) this.behavior.onRenderPass(event); } @Override public void onWorldEvent(WorldEvent event) { if (event.getWorld() != null && event.getState() == EventState.POST) { // Exiting the world, just destroy destroyBehaviorAsync(); } } @Override public void onChunkEvent(ChunkEvent event) { if (this.behavior != null) this.behavior.onChunkEvent(event); } @Override public void onBlockChange(BlockChangeEvent event) { if (this.behavior != null) this.behavior.onBlockChange(event); } @Override public void onReceivePacket(PacketEvent event) { if (this.behavior != null) this.behavior.onReceivePacket(event); } @Override public void onPostTick(TickEvent event) { IBaritoneProcess procThisTick = baritone.getPathingControlManager().mostRecentInControl().orElse(null); if (this.behavior != null && procThisTick == this) this.behavior.onPostTick(event); } /** * Custom calculation context which makes the player fall into lava */ public static final class WalkOffCalculationContext extends CalculationContext { public WalkOffCalculationContext(IBaritone baritone) { super(baritone, true); this.allowFallIntoLava = true; this.minFallHeight = 8; this.maxFallHeightNoWater = 10000; } @Override public double costOfPlacingAt(int x, int y, int z, BlockState current) { return COST_INF; } @Override public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { return COST_INF; } @Override public double placeBucketCost() { return COST_INF; } } private static boolean isInBounds(BlockPos pos) { return pos.getY() >= 0 && pos.getY() < 128; } private boolean isSafeBlock(Block block) { return block == Blocks.NETHERRACK || block == Blocks.GRAVEL || (block == Blocks.NETHER_BRICKS && Baritone.settings().elytraAllowLandOnNetherFortress.value); } private boolean isSafeBlock(BlockPos pos) { return isSafeBlock(ctx.world().getBlockState(pos).getBlock()); } private boolean isAtEdge(BlockPos pos) { return !isSafeBlock(pos.north()) || !isSafeBlock(pos.south()) || !isSafeBlock(pos.east()) || !isSafeBlock(pos.west()) // corners || !isSafeBlock(pos.north().west()) || !isSafeBlock(pos.north().east()) || !isSafeBlock(pos.south().west()) || !isSafeBlock(pos.south().east()); } private boolean isColumnAir(BlockPos landingSpot, int minHeight) { BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(landingSpot.getX(), landingSpot.getY(), landingSpot.getZ()); final int maxY = mut.getY() + minHeight; for (int y = mut.getY() + 1; y <= maxY; y++) { mut.set(mut.getX(), y, mut.getZ()); if (!(ctx.world().getBlockState(mut).getBlock() instanceof AirBlock)) { return false; } } return true; } private boolean hasAirBubble(BlockPos pos) { final int radius = 4; // Half of the full width, rounded down, as we're counting blocks in each direction from the center BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(); for (int x = -radius; x <= radius; x++) { for (int y = -radius; y <= radius; y++) { for (int z = -radius; z <= radius; z++) { mut.set(pos.getX() + x, pos.getY() + y, pos.getZ() + z); if (!(ctx.world().getBlockState(mut).getBlock() instanceof AirBlock)) { return false; } } } } return true; } private BetterBlockPos checkLandingSpot(BlockPos pos, LongOpenHashSet checkedSpots) { BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(pos.getX(), pos.getY(), pos.getZ()); while (mut.getY() >= 0) { if (checkedSpots.contains(mut.asLong())) { return null; } checkedSpots.add(mut.asLong()); Block block = ctx.world().getBlockState(mut).getBlock(); if (isSafeBlock(block)) { if (!isAtEdge(mut)) { return new BetterBlockPos(mut); } return null; } else if (block != Blocks.AIR) { return null; } mut.set(mut.getX(), mut.getY() - 1, mut.getZ()); } return null; // void } private static final int LANDING_COLUMN_HEIGHT = 15; private Set<BetterBlockPos> badLandingSpots = new HashSet<>(); private BetterBlockPos findSafeLandingSpot(BetterBlockPos start) { Queue<BetterBlockPos> queue = new PriorityQueue<>(Comparator.<BetterBlockPos>comparingInt(pos -> (pos.x - start.x) * (pos.x - start.x) + (pos.z - start.z) * (pos.z - start.z)).thenComparingInt(pos -> -pos.y)); Set<BetterBlockPos> visited = new HashSet<>(); LongOpenHashSet checkedPositions = new LongOpenHashSet(); queue.add(start); while (!queue.isEmpty()) { BetterBlockPos pos = queue.poll(); if (ctx.world().isLoaded(pos) && isInBounds(pos) && ctx.world().getBlockState(pos).getBlock() == Blocks.AIR) { BetterBlockPos actualLandingSpot = checkLandingSpot(pos, checkedPositions); if (actualLandingSpot != null && isColumnAir(actualLandingSpot, LANDING_COLUMN_HEIGHT) && hasAirBubble(actualLandingSpot.above(LANDING_COLUMN_HEIGHT)) && !badLandingSpots.contains(actualLandingSpot.above(LANDING_COLUMN_HEIGHT))) { return actualLandingSpot.above(LANDING_COLUMN_HEIGHT); } if (visited.add(pos.north())) queue.add(pos.north()); if (visited.add(pos.east())) queue.add(pos.east()); if (visited.add(pos.south())) queue.add(pos.south()); if (visited.add(pos.west())) queue.add(pos.west()); if (visited.add(pos.above())) queue.add(pos.above()); if (visited.add(pos.below())) queue.add(pos.below()); } } return null; } } ================================================ FILE: src/main/java/baritone/process/ExploreProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.cache.ICachedWorld; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalComposite; import baritone.api.pathing.goals.GoalXZ; import baritone.api.pathing.goals.GoalYLevel; import baritone.api.process.IExploreProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.utils.MyChunkPos; import baritone.cache.CachedWorld; import baritone.utils.BaritoneProcessHelper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; public final class ExploreProcess extends BaritoneProcessHelper implements IExploreProcess { private BlockPos explorationOrigin; private IChunkFilter filter; private int distanceCompleted; public ExploreProcess(Baritone baritone) { super(baritone); } @Override public boolean isActive() { return explorationOrigin != null; } @Override public void explore(int centerX, int centerZ) { explorationOrigin = new BlockPos(centerX, 0, centerZ); distanceCompleted = 0; } @Override public void applyJsonFilter(Path path, boolean invert) throws Exception { filter = new JsonChunkFilter(path, invert); } public IChunkFilter calcFilter() { IChunkFilter filter; if (this.filter != null) { filter = new EitherChunk(this.filter, new BaritoneChunkCache()); } else { filter = new BaritoneChunkCache(); } return filter; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (calcFailed) { logDirect("Failed"); if (Baritone.settings().notificationOnExploreFinished.value) { logNotification("Exploration failed", true); } onLostControl(); return null; } IChunkFilter filter = calcFilter(); if (!Baritone.settings().disableCompletionCheck.value && filter.countRemain() == 0) { logDirect("Explored all chunks"); if (Baritone.settings().notificationOnExploreFinished.value) { logNotification("Explored all chunks", false); } onLostControl(); return null; } Goal[] closestUncached = closestUncachedChunks(explorationOrigin, filter); if (closestUncached == null) { logDebug("awaiting region load from disk"); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } return new PathingCommand(new GoalComposite(closestUncached), PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); } private Goal[] closestUncachedChunks(BlockPos center, IChunkFilter filter) { int chunkX = center.getX() >> 4; int chunkZ = center.getZ() >> 4; int count = Math.min(filter.countRemain(), Baritone.settings().exploreChunkSetMinimumSize.value); List<BlockPos> centers = new ArrayList<>(); int renderDistance = Baritone.settings().worldExploringChunkOffset.value; for (int dist = distanceCompleted; ; dist++) { for (int dx = -dist; dx <= dist; dx++) { int zval = dist - Math.abs(dx); for (int mult = 0; mult < 2; mult++) { int dz = (mult * 2 - 1) * zval; // dz can be either -zval or zval int trueDist = Math.abs(dx) + Math.abs(dz); if (trueDist != dist) { throw new IllegalStateException(String.format( "Offset %s %s has distance %s, expected %s", dx, dz, trueDist, dist)); } switch (filter.isAlreadyExplored(chunkX + dx, chunkZ + dz)) { case UNKNOWN: return null; // awaiting load case NOT_EXPLORED: break; // note: this breaks the switch not the for case EXPLORED: continue; // note: this continues the for default: } int centerX = ((chunkX + dx) << 4) + 8; int centerZ = ((chunkZ + dz) << 4) + 8; int offset = renderDistance << 4; if (dx < 0) { centerX -= offset; } else { centerX += offset; } if (dz < 0) { centerZ -= offset; } else { centerZ += offset; } centers.add(new BlockPos(centerX, 0, centerZ)); } } if (dist % 10 == 0) { count = Math.min(filter.countRemain(), Baritone.settings().exploreChunkSetMinimumSize.value); } if (centers.size() >= count) { return centers.stream().map(pos -> createGoal(pos.getX(), pos.getZ())).toArray(Goal[]::new); } if (centers.isEmpty()) { // we have explored everything from 0 to dist inclusive // next time we should start our check at dist+1 distanceCompleted = dist + 1; } } } private static Goal createGoal(int x, int z) { if (Baritone.settings().exploreMaintainY.value == -1) { return new GoalXZ(x, z); } // don't use a goalblock because we still want isInGoal to return true if X and Z are correct // we just want to try and maintain Y on the way there, not necessarily end at that specific Y return new GoalXZ(x, z) { @Override public double heuristic(int x, int y, int z) { return super.heuristic(x, y, z) + GoalYLevel.calculate(Baritone.settings().exploreMaintainY.value, y); } }; } private enum Status { EXPLORED, NOT_EXPLORED, UNKNOWN; } private interface IChunkFilter { Status isAlreadyExplored(int chunkX, int chunkZ); int countRemain(); } private class BaritoneChunkCache implements IChunkFilter { private final ICachedWorld cache = baritone.getWorldProvider().getCurrentWorld().getCachedWorld(); @Override public Status isAlreadyExplored(int chunkX, int chunkZ) { int centerX = chunkX << 4; int centerZ = chunkZ << 4; if (cache.isCached(centerX, centerZ)) { return Status.EXPLORED; } if (!((CachedWorld) cache).regionLoaded(centerX, centerZ)) { Baritone.getExecutor().execute(() -> { ((CachedWorld) cache).tryLoadFromDisk(centerX >> 9, centerZ >> 9); }); return Status.UNKNOWN; // we still need to load regions from disk in order to decide properly } return Status.NOT_EXPLORED; } @Override public int countRemain() { return Integer.MAX_VALUE; } } private class JsonChunkFilter implements IChunkFilter { private final boolean invert; // if true, the list is interpreted as a list of chunks that are NOT explored, if false, the list is interpreted as a list of chunks that ARE explored private final LongOpenHashSet inFilter; private final MyChunkPos[] positions; private JsonChunkFilter(Path path, boolean invert) throws Exception { // ioexception, json exception, etc this.invert = invert; Gson gson = new GsonBuilder().create(); positions = gson.fromJson(new InputStreamReader(Files.newInputStream(path)), MyChunkPos[].class); logDirect("Loaded " + positions.length + " positions"); inFilter = new LongOpenHashSet(); for (MyChunkPos mcp : positions) { inFilter.add(ChunkPos.asLong(mcp.x, mcp.z)); } } @Override public Status isAlreadyExplored(int chunkX, int chunkZ) { if (inFilter.contains(ChunkPos.asLong(chunkX, chunkZ)) ^ invert) { // either it's on the list of explored chunks, or it's not on the list of unexplored chunks // either way, we have it return Status.EXPLORED; } else { // either it's not on the list of explored chunks, or it's on the list of unexplored chunks // either way, it depends on if baritone has cached it so defer to that return Status.UNKNOWN; } } @Override public int countRemain() { if (!invert) { // if invert is false, anything not on the list is uncached return Integer.MAX_VALUE; } // but if invert is true, anything not on the list IS assumed cached // so we are done if everything on our list is cached! int countRemain = 0; BaritoneChunkCache bcc = new BaritoneChunkCache(); for (MyChunkPos pos : positions) { if (bcc.isAlreadyExplored(pos.x, pos.z) != Status.EXPLORED) { // either waiting for it or dont have it at all countRemain++; if (countRemain >= Baritone.settings().exploreChunkSetMinimumSize.value) { return countRemain; } } } return countRemain; } } private class EitherChunk implements IChunkFilter { private final IChunkFilter a; private final IChunkFilter b; private EitherChunk(IChunkFilter a, IChunkFilter b) { this.a = a; this.b = b; } @Override public Status isAlreadyExplored(int chunkX, int chunkZ) { if (a.isAlreadyExplored(chunkX, chunkZ) == Status.EXPLORED) { return Status.EXPLORED; } return b.isAlreadyExplored(chunkX, chunkZ); } @Override public int countRemain() { return Math.min(a.countRemain(), b.countRemain()); } } @Override public void onLostControl() { explorationOrigin = null; } @Override public String displayName0() { return "Exploring around " + explorationOrigin + ", distance completed " + distanceCompleted + ", currently going to " + new GoalComposite(closestUncachedChunks(explorationOrigin, calcFilter())); } } ================================================ FILE: src/main/java/baritone/process/FarmProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.pathing.goals.GoalGetToBlock; import baritone.api.pathing.goals.GoalComposite; import baritone.api.process.IFarmProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.RayTraceUtils; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.BambooStalkBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.BonemealableBlock; import net.minecraft.world.level.block.CactusBlock; import net.minecraft.world.level.block.CocoaBlock; import net.minecraft.world.level.block.CropBlock; import net.minecraft.world.level.block.NetherWartBlock; import net.minecraft.world.level.block.SugarCaneBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.function.Predicate; public final class FarmProcess extends BaritoneProcessHelper implements IFarmProcess { private boolean active; private List<BlockPos> locations; private int tickCount; private int range; private BlockPos center; private static final List<Item> FARMLAND_PLANTABLE = Arrays.asList( Items.BEETROOT_SEEDS, Items.MELON_SEEDS, Items.WHEAT_SEEDS, Items.PUMPKIN_SEEDS, Items.POTATO, Items.CARROT ); private static final List<Item> PICKUP_DROPPED = Arrays.asList( Items.BEETROOT_SEEDS, Items.BEETROOT, Items.MELON_SEEDS, Items.MELON_SLICE, Blocks.MELON.asItem(), Items.WHEAT_SEEDS, Items.WHEAT, Items.PUMPKIN_SEEDS, Blocks.PUMPKIN.asItem(), Items.POTATO, Items.CARROT, Items.NETHER_WART, Items.COCOA_BEANS, Blocks.SUGAR_CANE.asItem(), Blocks.BAMBOO.asItem(), Blocks.CACTUS.asItem() ); public FarmProcess(Baritone baritone) { super(baritone); } @Override public boolean isActive() { return active; } @Override public void farm(int range, BlockPos pos) { if (pos == null) { center = baritone.getPlayerContext().playerFeet(); } else { center = pos; } this.range = range; active = true; locations = null; } private enum Harvest { WHEAT((CropBlock) Blocks.WHEAT), CARROTS((CropBlock) Blocks.CARROTS), POTATOES((CropBlock) Blocks.POTATOES), BEETROOT((CropBlock) Blocks.BEETROOTS), PUMPKIN(Blocks.PUMPKIN, state -> true), MELON(Blocks.MELON, state -> true), NETHERWART(Blocks.NETHER_WART, state -> state.getValue(NetherWartBlock.AGE) >= 3), COCOA(Blocks.COCOA, state -> state.getValue(CocoaBlock.AGE) >= 2), SUGARCANE(Blocks.SUGAR_CANE, null) { @Override public boolean readyToHarvest(Level world, BlockPos pos, BlockState state) { if (Baritone.settings().replantCrops.value) { return world.getBlockState(pos.below()).getBlock() instanceof SugarCaneBlock; } return true; } }, BAMBOO(Blocks.BAMBOO, null) { @Override public boolean readyToHarvest(Level world, BlockPos pos, BlockState state) { if (Baritone.settings().replantCrops.value) { return world.getBlockState(pos.below()).getBlock() instanceof BambooStalkBlock; } return true; } }, CACTUS(Blocks.CACTUS, null) { @Override public boolean readyToHarvest(Level world, BlockPos pos, BlockState state) { if (Baritone.settings().replantCrops.value) { return world.getBlockState(pos.below()).getBlock() instanceof CactusBlock; } return true; } }; public final Block block; public final Predicate<BlockState> readyToHarvest; Harvest(CropBlock blockCrops) { this(blockCrops, blockCrops::isMaxAge); // max age is 7 for wheat, carrots, and potatoes, but 3 for beetroot } Harvest(Block block, Predicate<BlockState> readyToHarvest) { this.block = block; this.readyToHarvest = readyToHarvest; } public boolean readyToHarvest(Level world, BlockPos pos, BlockState state) { return readyToHarvest.test(state); } } private boolean readyForHarvest(Level world, BlockPos pos, BlockState state) { for (Harvest harvest : Harvest.values()) { if (harvest.block == state.getBlock()) { return harvest.readyToHarvest(world, pos, state); } } return false; } private boolean isPlantable(ItemStack stack) { return FARMLAND_PLANTABLE.contains(stack.getItem()); } private boolean isBoneMeal(ItemStack stack) { return !stack.isEmpty() && stack.getItem().equals(Items.BONE_MEAL); } private boolean isNetherWart(ItemStack stack) { return !stack.isEmpty() && stack.getItem().equals(Items.NETHER_WART); } private boolean isCocoa(ItemStack stack) { return !stack.isEmpty() && stack.getItem().equals(Items.COCOA_BEANS); } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (Baritone.settings().mineGoalUpdateInterval.value != 0 && tickCount++ % Baritone.settings().mineGoalUpdateInterval.value == 0) { ArrayList<Block> scan = new ArrayList<>(); for (Harvest harvest : Harvest.values()) { scan.add(harvest.block); } if (Baritone.settings().replantCrops.value) { scan.add(Blocks.FARMLAND); scan.add(Blocks.JUNGLE_LOG); if (Baritone.settings().replantNetherWart.value) { scan.add(Blocks.SOUL_SAND); } } Baritone.getExecutor().execute(() -> locations = BaritoneAPI.getProvider().getWorldScanner().scanChunkRadius(ctx, scan, Baritone.settings().farmMaxScanSize.value, 10, 10)); } if (locations == null) { return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } List<BlockPos> toBreak = new ArrayList<>(); List<BlockPos> openFarmland = new ArrayList<>(); List<BlockPos> bonemealable = new ArrayList<>(); List<BlockPos> openSoulsand = new ArrayList<>(); List<BlockPos> openLog = new ArrayList<>(); for (BlockPos pos : locations) { //check if the target block is out of range. if (range != 0 && pos.distSqr(center) > range * range) { continue; } BlockState state = ctx.world().getBlockState(pos); boolean airAbove = ctx.world().getBlockState(pos.above()).getBlock() instanceof AirBlock; if (state.getBlock() == Blocks.FARMLAND) { if (airAbove) { openFarmland.add(pos); } continue; } if (state.getBlock() == Blocks.SOUL_SAND) { if (airAbove) { openSoulsand.add(pos); } continue; } if (state.getBlock() == Blocks.JUNGLE_LOG) { for (Direction direction : Direction.Plane.HORIZONTAL) { if (ctx.world().getBlockState(pos.relative(direction)).getBlock() instanceof AirBlock) { openLog.add(pos); break; } } continue; } if (readyForHarvest(ctx.world(), pos, state)) { toBreak.add(pos); continue; } if (state.getBlock() instanceof BonemealableBlock) { BonemealableBlock ig = (BonemealableBlock) state.getBlock(); if (ig.isValidBonemealTarget(ctx.world(), pos, state, true) && ig.isBonemealSuccess(ctx.world(), ctx.world().random, pos, state)) { bonemealable.add(pos); } } } baritone.getInputOverrideHandler().clearAllKeys(); BetterBlockPos playerPos = ctx.playerFeet(); double blockReachDistance = ctx.playerController().getBlockReachDistance(); for (BlockPos pos : toBreak) { if (playerPos.distSqr(pos) > blockReachDistance * blockReachDistance) { continue; } Optional<Rotation> rot = RotationUtils.reachable(ctx, pos); if (rot.isPresent() && isSafeToCancel) { baritone.getLookBehavior().updateTarget(rot.get(), true); MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } ArrayList<BlockPos> both = new ArrayList<>(openFarmland); both.addAll(openSoulsand); for (BlockPos pos : both) { if (playerPos.distSqr(pos) > blockReachDistance * blockReachDistance) { continue; } boolean soulsand = openSoulsand.contains(pos); Optional<Rotation> rot = RotationUtils.reachableOffset(ctx, pos, new Vec3(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), blockReachDistance, false); if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, soulsand ? this::isNetherWart : this::isPlantable)) { HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), blockReachDistance); if (result instanceof BlockHitResult && ((BlockHitResult) result).getDirection() == Direction.UP) { baritone.getLookBehavior().updateTarget(rot.get(), true); if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } } for (BlockPos pos : openLog) { if (playerPos.distSqr(pos) > blockReachDistance * blockReachDistance) { continue; } for (Direction dir : Direction.Plane.HORIZONTAL) { if (!(ctx.world().getBlockState(pos.relative(dir)).getBlock() instanceof AirBlock)) { continue; } Vec3 faceCenter = Vec3.atCenterOf(pos).add(Vec3.atLowerCornerOf(dir.getNormal()).scale(0.5)); Optional<Rotation> rot = RotationUtils.reachableOffset(ctx, pos, faceCenter, blockReachDistance, false); if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isCocoa)) { HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), blockReachDistance); if (result instanceof BlockHitResult && ((BlockHitResult) result).getDirection() == dir) { baritone.getLookBehavior().updateTarget(rot.get(), true); if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } } } for (BlockPos pos : bonemealable) { if (playerPos.distSqr(pos) > blockReachDistance * blockReachDistance) { continue; } Optional<Rotation> rot = RotationUtils.reachable(ctx, pos); if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isBoneMeal)) { baritone.getLookBehavior().updateTarget(rot.get(), true); if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } if (calcFailed) { logDirect("Farm failed"); if (Baritone.settings().notificationOnFarmFail.value) { logNotification("Farm failed", true); } onLostControl(); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } List<Goal> goalz = new ArrayList<>(); for (BlockPos pos : toBreak) { goalz.add(new BuilderProcess.GoalBreak(pos)); } if (baritone.getInventoryBehavior().throwaway(false, this::isPlantable)) { for (BlockPos pos : openFarmland) { goalz.add(new GoalBlock(pos.above())); } } if (baritone.getInventoryBehavior().throwaway(false, this::isNetherWart)) { for (BlockPos pos : openSoulsand) { goalz.add(new GoalBlock(pos.above())); } } if (baritone.getInventoryBehavior().throwaway(false, this::isCocoa)) { for (BlockPos pos : openLog) { for (Direction direction : Direction.Plane.HORIZONTAL) { if (ctx.world().getBlockState(pos.relative(direction)).getBlock() instanceof AirBlock) { goalz.add(new GoalGetToBlock(pos.relative(direction))); } } } } if (baritone.getInventoryBehavior().throwaway(false, this::isBoneMeal)) { for (BlockPos pos : bonemealable) { goalz.add(new GoalBlock(pos)); } } for (Entity entity : ctx.entities()) { if (entity instanceof ItemEntity && entity.isOnGround()) { ItemEntity ei = (ItemEntity) entity; if (PICKUP_DROPPED.contains(ei.getItem().getItem())) { // +0.1 because of farmland's 0.9375 dummy height lol goalz.add(new GoalBlock(new BetterBlockPos(entity.position().x, entity.position().y + 0.1, entity.position().z))); } } } if (goalz.isEmpty()) { logDirect("Farm failed"); if (Baritone.settings().notificationOnFarmFail.value) { logNotification("Farm failed", true); } onLostControl(); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } return new PathingCommand(new GoalComposite(goalz.toArray(new Goal[0])), PathingCommandType.SET_GOAL_AND_PATH); } @Override public void onLostControl() { active = false; } @Override public String displayName0() { return "Farming"; } } ================================================ FILE: src/main/java/baritone/process/FollowProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.pathing.goals.GoalComposite; import baritone.api.pathing.goals.GoalNear; import baritone.api.pathing.goals.GoalXZ; import baritone.api.process.IFollowProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.utils.BetterBlockPos; import baritone.utils.BaritoneProcessHelper; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; /** * Follow an entity * * @author leijurv */ public final class FollowProcess extends BaritoneProcessHelper implements IFollowProcess { private Predicate<Entity> filter; private List<Entity> cache; private boolean into; // walk straight into the target, regardless of settings public FollowProcess(Baritone baritone) { super(baritone); } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { scanWorld(); Goal goal = new GoalComposite(cache.stream().map(this::towards).toArray(Goal[]::new)); return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH); } private Goal towards(Entity following) { BlockPos pos; if (Baritone.settings().followOffsetDistance.value == 0 || into) { pos = following.blockPosition(); } else { GoalXZ g = GoalXZ.fromDirection(following.position(), Baritone.settings().followOffsetDirection.value, Baritone.settings().followOffsetDistance.value); pos = new BetterBlockPos(g.getX(), following.position().y, g.getZ()); } if (into) { return new GoalBlock(pos); } return new GoalNear(pos, Baritone.settings().followRadius.value); } private boolean followable(Entity entity) { if (entity == null) { return false; } if (!entity.isAlive()) { return false; } if (entity.equals(ctx.player())) { return false; } int maxDist = Baritone.settings().followTargetMaxDistance.value; if (maxDist != 0 && entity.distanceToSqr(ctx.player()) > maxDist * maxDist) { return false; } return ctx.entitiesStream().anyMatch(entity::equals); } private void scanWorld() { cache = ctx.entitiesStream() .filter(this::followable) .filter(this.filter) .distinct() .collect(Collectors.toList()); } @Override public boolean isActive() { if (filter == null) { return false; } scanWorld(); return !cache.isEmpty(); } @Override public void onLostControl() { filter = null; cache = null; } @Override public String displayName0() { return "Following " + cache; } @Override public void follow(Predicate<Entity> filter) { this.filter = filter; this.into = false; } @Override public void pickup(Predicate<ItemStack> filter) { this.filter = e -> e instanceof ItemEntity && filter.test(((ItemEntity) e).getItem()); this.into = true; } @Override public List<Entity> following() { return cache; } @Override public Predicate<Entity> currentFilter() { return filter; } } ================================================ FILE: src/main/java/baritone/process/GetToBlockProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.pathing.goals.*; import baritone.api.process.IGetToBlockProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.utils.BlockOptionalMeta; import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import net.minecraft.core.BlockPos; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import java.util.*; public final class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBlockProcess { private BlockOptionalMeta gettingTo; private List<BlockPos> knownLocations; private List<BlockPos> blacklist; // locations we failed to calc to private BlockPos start; private int tickCount = 0; private int arrivalTickCount = 0; public GetToBlockProcess(Baritone baritone) { super(baritone); } @Override public void getToBlock(BlockOptionalMeta block) { onLostControl(); gettingTo = block; start = ctx.playerFeet(); blacklist = new ArrayList<>(); arrivalTickCount = 0; rescan(new ArrayList<>(), new GetToBlockCalculationContext(false)); } @Override public boolean isActive() { return gettingTo != null; } @Override public synchronized PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (knownLocations == null) { rescan(new ArrayList<>(), new GetToBlockCalculationContext(false)); } if (knownLocations.isEmpty()) { if (Baritone.settings().exploreForBlocks.value && !calcFailed) { return new PathingCommand(new GoalRunAway(1, start) { @Override public boolean isInGoal(int x, int y, int z) { return false; } @Override public double heuristic() { return Double.NEGATIVE_INFINITY; } }, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); } logDirect("No known locations of " + gettingTo + ", canceling GetToBlock"); if (isSafeToCancel) { onLostControl(); } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } Goal goal = new GoalComposite(knownLocations.stream().map(this::createGoal).toArray(Goal[]::new)); if (calcFailed) { if (Baritone.settings().blacklistClosestOnFailure.value) { logDirect("Unable to find any path to " + gettingTo + ", blacklisting presumably unreachable closest instances..."); blacklistClosest(); return onTick(false, isSafeToCancel); // gamer moment } else { logDirect("Unable to find any path to " + gettingTo + ", canceling GetToBlock"); if (isSafeToCancel) { onLostControl(); } return new PathingCommand(goal, PathingCommandType.CANCEL_AND_SET_GOAL); } } int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.value; if (mineGoalUpdateInterval != 0 && tickCount++ % mineGoalUpdateInterval == 0) { // big brain List<BlockPos> current = new ArrayList<>(knownLocations); CalculationContext context = new GetToBlockCalculationContext(true); Baritone.getExecutor().execute(() -> rescan(current, context)); } if (goal.isInGoal(ctx.playerFeet()) && goal.isInGoal(baritone.getPathingBehavior().pathStart()) && isSafeToCancel) { // we're there if (rightClickOnArrival(gettingTo.getBlock())) { if (rightClick()) { onLostControl(); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } } else { onLostControl(); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } } return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH); } // blacklist the closest block and its adjacent blocks public synchronized boolean blacklistClosest() { List<BlockPos> newBlacklist = new ArrayList<>(); knownLocations.stream().min(Comparator.comparingDouble(ctx.playerFeet()::distSqr)).ifPresent(newBlacklist::add); outer: while (true) { for (BlockPos known : knownLocations) { for (BlockPos blacklist : newBlacklist) { if (areAdjacent(known, blacklist)) { // directly adjacent newBlacklist.add(known); knownLocations.remove(known); continue outer; } } } // i can't do break; (codacy gets mad), and i can't do if(true){break}; (codacy gets mad) // so i will do this switch (newBlacklist.size()) { default: break outer; } } logDebug("Blacklisting unreachable locations " + newBlacklist); blacklist.addAll(newBlacklist); return !newBlacklist.isEmpty(); } // this is to signal to MineProcess that we don't care about the allowBreak setting // it is NOT to be used to actually calculate a path public class GetToBlockCalculationContext extends CalculationContext { public GetToBlockCalculationContext(boolean forUseOnAnotherThread) { super(GetToBlockProcess.super.baritone, forUseOnAnotherThread); } @Override public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { return 1; } } // safer than direct double comparison from distanceSq private boolean areAdjacent(BlockPos posA, BlockPos posB) { int diffX = Math.abs(posA.getX() - posB.getX()); int diffY = Math.abs(posA.getY() - posB.getY()); int diffZ = Math.abs(posA.getZ() - posB.getZ()); return (diffX + diffY + diffZ) == 1; } @Override public synchronized void onLostControl() { gettingTo = null; knownLocations = null; start = null; blacklist = null; baritone.getInputOverrideHandler().clearAllKeys(); } @Override public String displayName0() { if (knownLocations.isEmpty()) { return "Exploring randomly to find " + gettingTo + ", no known locations"; } return "Get To " + gettingTo + ", " + knownLocations.size() + " known locations"; } private synchronized void rescan(List<BlockPos> known, CalculationContext context) { List<BlockPos> positions = MineProcess.searchWorld(context, new BlockOptionalMetaLookup(gettingTo), 64, known, blacklist, Collections.emptyList()); positions.removeIf(blacklist::contains); knownLocations = positions; } private Goal createGoal(BlockPos pos) { if (walkIntoInsteadOfAdjacent(gettingTo.getBlock())) { return new GoalTwoBlocks(pos); } if (blockOnTopMustBeRemoved(gettingTo.getBlock()) && MovementHelper.isBlockNormalCube(baritone.bsi.get0(pos.above()))) { // TODO this should be the check for chest openability return new GoalBlock(pos.above()); } return new GoalGetToBlock(pos); } private boolean rightClick() { for (BlockPos pos : knownLocations) { Optional<Rotation> reachable = RotationUtils.reachable(ctx, pos, ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { baritone.getLookBehavior().updateTarget(reachable.get(), true); if (knownLocations.contains(ctx.getSelectedBlock().orElse(null))) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); // TODO find some way to right click even if we're in an ESC menu System.out.println(ctx.player().containerMenu); if (!(ctx.player().containerMenu instanceof InventoryMenu)) { return true; } } if (arrivalTickCount++ > 20) { logDirect("Right click timed out"); return true; } return false; // trying to right click, will do it next tick or so } } logDirect("Arrived but failed to right click open"); return true; } private boolean walkIntoInsteadOfAdjacent(Block block) { if (!Baritone.settings().enterPortal.value) { return false; } return block == Blocks.NETHER_PORTAL; } private boolean rightClickOnArrival(Block block) { if (!Baritone.settings().rightClickContainerOnArrival.value) { return false; } return block == Blocks.CRAFTING_TABLE || block == Blocks.FURNACE || block == Blocks.BLAST_FURNACE || block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST; } private boolean blockOnTopMustBeRemoved(Block block) { if (!rightClickOnArrival(block)) { // only if we plan to actually open it on arrival return false; } // only these chests; you can open a crafting table or furnace even with a block on top return block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST; } } ================================================ FILE: src/main/java/baritone/process/InventoryPauserProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.utils.BaritoneProcessHelper; public class InventoryPauserProcess extends BaritoneProcessHelper { boolean pauseRequestedLastTick; boolean safeToCancelLastTick; int ticksOfStationary; public InventoryPauserProcess(Baritone baritone) { super(baritone); } @Override public boolean isActive() { if (ctx.player() == null || ctx.world() == null) { return false; } return true; } private double motion() { return ctx.player().getDeltaMovement().multiply(1, 0, 1).length(); } private boolean stationaryNow() { return motion() < 0.00001; } public boolean stationaryForInventoryMove() { pauseRequestedLastTick = true; return safeToCancelLastTick && ticksOfStationary > 1; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { //logDebug(pauseRequestedLastTick + " " + safeToCancelLastTick + " " + ticksOfStationary); safeToCancelLastTick = isSafeToCancel; if (pauseRequestedLastTick) { pauseRequestedLastTick = false; if (stationaryNow()) { ticksOfStationary++; } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } ticksOfStationary = 0; return new PathingCommand(null, PathingCommandType.DEFER); } @Override public void onLostControl() { } @Override public String displayName0() { return "inventory pauser"; } @Override public double priority() { return 5.1; // slightly higher than backfill } @Override public boolean isTemporary() { return true; } } ================================================ FILE: src/main/java/baritone/process/MineProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.pathing.goals.*; import baritone.api.process.IMineProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.cache.CachedChunk; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.FallingBlock; import net.minecraft.world.level.block.state.BlockState; import java.util.*; import java.util.stream.Collectors; import static baritone.api.pathing.movement.ActionCosts.COST_INF; /** * Mine blocks of a certain type * * @author leijurv */ public final class MineProcess extends BaritoneProcessHelper implements IMineProcess { private BlockOptionalMetaLookup filter; private List<BlockPos> knownOreLocations; private List<BlockPos> blacklist; // inaccessible private Map<BlockPos, Long> anticipatedDrops; private BlockPos branchPoint; private GoalRunAway branchPointRunaway; private int desiredQuantity; private int tickCount; public MineProcess(Baritone baritone) { super(baritone); } @Override public boolean isActive() { return filter != null; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (desiredQuantity > 0) { int curr = ctx.player().getInventory().items.stream() .filter(stack -> filter.has(stack)) .mapToInt(ItemStack::getCount).sum(); if (curr >= desiredQuantity) { logDirect("Have " + curr + " valid items"); cancel(); return null; } } if (calcFailed) { if (!knownOreLocations.isEmpty() && Baritone.settings().blacklistClosestOnFailure.value) { logDirect("Unable to find any path to " + filter + ", blacklisting presumably unreachable closest instance..."); if (Baritone.settings().notificationOnMineFail.value) { logNotification("Unable to find any path to " + filter + ", blacklisting presumably unreachable closest instance...", true); } knownOreLocations.stream().min(Comparator.comparingDouble(ctx.playerFeet()::distSqr)).ifPresent(blacklist::add); knownOreLocations.removeIf(blacklist::contains); } else { logDirect("Unable to find any path to " + filter + ", canceling mine"); if (Baritone.settings().notificationOnMineFail.value) { logNotification("Unable to find any path to " + filter + ", canceling mine", true); } cancel(); return null; } } updateLoucaSystem(); int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.value; List<BlockPos> curr = new ArrayList<>(knownOreLocations); if (mineGoalUpdateInterval != 0 && tickCount++ % mineGoalUpdateInterval == 0) { // big brain CalculationContext context = new CalculationContext(baritone, true); Baritone.getExecutor().execute(() -> rescan(curr, context)); } if (Baritone.settings().legitMine.value) { if (!addNearby()) { cancel(); return null; } } Optional<BlockPos> shaft = curr.stream() .filter(pos -> pos.getX() == ctx.playerFeet().getX() && pos.getZ() == ctx.playerFeet().getZ()) .filter(pos -> pos.getY() >= ctx.playerFeet().getY()) .filter(pos -> !(BlockStateInterface.get(ctx, pos).getBlock() instanceof AirBlock)) // after breaking a block, it takes mineGoalUpdateInterval ticks for it to actually update this list =( .min(Comparator.comparingDouble(ctx.playerFeet().above()::distSqr)); baritone.getInputOverrideHandler().clearAllKeys(); if (shaft.isPresent() && ctx.player().isOnGround()) { BlockPos pos = shaft.get(); BlockState state = baritone.bsi.get0(pos); if (!MovementHelper.avoidBreaking(baritone.bsi, pos.getX(), pos.getY(), pos.getZ(), state)) { Optional<Rotation> rot = RotationUtils.reachable(ctx, pos); if (rot.isPresent() && isSafeToCancel) { baritone.getLookBehavior().updateTarget(rot.get(), true); MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); if (ctx.isLookingAt(pos) || ctx.playerRotations().isReallyCloseTo(rot.get())) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } } PathingCommand command = updateGoal(); if (command == null) { // none in range // maybe say something in chat? (ahem impact) cancel(); return null; } return command; } private void updateLoucaSystem() { Map<BlockPos, Long> copy = new HashMap<>(anticipatedDrops); ctx.getSelectedBlock().ifPresent(pos -> { if (knownOreLocations.contains(pos)) { copy.put(pos, System.currentTimeMillis() + Baritone.settings().mineDropLoiterDurationMSThanksLouca.value); } }); // elaborate dance to avoid concurrentmodificationexcepption since rescan thread reads this // don't want to slow everything down with a gross lock do we now for (BlockPos pos : anticipatedDrops.keySet()) { if (copy.get(pos) < System.currentTimeMillis()) { copy.remove(pos); } } anticipatedDrops = copy; } @Override public void onLostControl() { mine(0, (BlockOptionalMetaLookup) null); } @Override public String displayName0() { return "Mine " + filter; } private PathingCommand updateGoal() { BlockOptionalMetaLookup filter = filterFilter(); if (filter == null) { return null; } boolean legit = Baritone.settings().legitMine.value; List<BlockPos> locs = knownOreLocations; if (!locs.isEmpty()) { CalculationContext context = new CalculationContext(baritone); List<BlockPos> locs2 = prune(context, new ArrayList<>(locs), filter, Baritone.settings().mineMaxOreLocationsCount.value, blacklist, droppedItemsScan()); // can't reassign locs, gotta make a new var locs2, because we use it in a lambda right here, and variables you use in a lambda must be effectively final Goal goal = new GoalComposite(locs2.stream().map(loc -> coalesce(loc, locs2, context)).toArray(Goal[]::new)); knownOreLocations = locs2; return new PathingCommand(goal, legit ? PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH : PathingCommandType.REVALIDATE_GOAL_AND_PATH); } // we don't know any ore locations at the moment if (!legit && !Baritone.settings().exploreForBlocks.value) { return null; } // only when we should explore for blocks or are in legit mode we do this int y = Baritone.settings().legitMineYLevel.value; if (branchPoint == null) { /*if (!baritone.getPathingBehavior().isPathing() && playerFeet().y == y) { // cool, path is over and we are at desired y branchPoint = playerFeet(); branchPointRunaway = null; } else { return new GoalYLevel(y); }*/ branchPoint = ctx.playerFeet(); } // TODO shaft mode, mine 1x1 shafts to either side // TODO also, see if the GoalRunAway with maintain Y at 11 works even from the surface if (branchPointRunaway == null) { branchPointRunaway = new GoalRunAway(1, y, branchPoint) { @Override public boolean isInGoal(int x, int y, int z) { return false; } @Override public double heuristic() { return Double.NEGATIVE_INFINITY; } }; } return new PathingCommand(branchPointRunaway, PathingCommandType.REVALIDATE_GOAL_AND_PATH); } private void rescan(List<BlockPos> already, CalculationContext context) { BlockOptionalMetaLookup filter = filterFilter(); if (filter == null) { return; } if (Baritone.settings().legitMine.value) { return; } List<BlockPos> dropped = droppedItemsScan(); List<BlockPos> locs = searchWorld(context, filter, Baritone.settings().mineMaxOreLocationsCount.value, already, blacklist, dropped); locs.addAll(dropped); if (locs.isEmpty() && !Baritone.settings().exploreForBlocks.value) { logDirect("No locations for " + filter + " known, cancelling"); if (Baritone.settings().notificationOnMineFail.value) { logNotification("No locations for " + filter + " known, cancelling", true); } cancel(); return; } knownOreLocations = locs; } private boolean internalMiningGoal(BlockPos pos, CalculationContext context, List<BlockPos> locs) { // Here, BlockStateInterface is used because the position may be in a cached chunk (the targeted block is one that is kept track of) if (locs.contains(pos)) { return true; } BlockState state = context.bsi.get0(pos); if (Baritone.settings().internalMiningAirException.value && state.getBlock() instanceof AirBlock) { return true; } return filter.has(state) && plausibleToBreak(context, pos); } private Goal coalesce(BlockPos loc, List<BlockPos> locs, CalculationContext context) { boolean assumeVerticalShaftMine = !(baritone.bsi.get0(loc.above()).getBlock() instanceof FallingBlock); if (!Baritone.settings().forceInternalMining.value) { if (assumeVerticalShaftMine) { // we can get directly below the block return new GoalThreeBlocks(loc); } else { // we need to get feet or head into the block return new GoalTwoBlocks(loc); } } boolean upwardGoal = internalMiningGoal(loc.above(), context, locs); boolean downwardGoal = internalMiningGoal(loc.below(), context, locs); boolean doubleDownwardGoal = internalMiningGoal(loc.below(2), context, locs); if (upwardGoal == downwardGoal) { // symmetric if (doubleDownwardGoal && assumeVerticalShaftMine) { // we have a checkerboard like pattern // this one, and the one two below it // therefore it's fine to path to immediately below this one, since your feet will be in the doubleDownwardGoal // but only if assumeVerticalShaftMine return new GoalThreeBlocks(loc); } else { // this block has nothing interesting two below, but is symmetric vertically so we can get either feet or head into it return new GoalTwoBlocks(loc); } } if (upwardGoal) { // downwardGoal known to be false // ignore the gap then potential doubleDownward, because we want to path feet into this one and head into upwardGoal return new GoalBlock(loc); } // upwardGoal known to be false, downwardGoal known to be true if (doubleDownwardGoal && assumeVerticalShaftMine) { // this block and two below it are goals // path into the center of the one below, because that includes directly below this one return new GoalTwoBlocks(loc.below()); } // upwardGoal false, downwardGoal true, doubleDownwardGoal false // just this block and the one immediately below, no others return new GoalBlock(loc.below()); } private static class GoalThreeBlocks extends GoalTwoBlocks { public GoalThreeBlocks(BlockPos pos) { super(pos); } @Override public boolean isInGoal(int x, int y, int z) { return x == this.x && (y == this.y || y == this.y - 1 || y == this.y - 2) && z == this.z; } @Override public double heuristic(int x, int y, int z) { int xDiff = x - this.x; int yDiff = y - this.y; int zDiff = z - this.z; return GoalBlock.calculate(xDiff, yDiff < -1 ? yDiff + 2 : yDiff == -1 ? 0 : yDiff, zDiff); } @Override public boolean equals(Object o) { return super.equals(o); } @Override public int hashCode() { return super.hashCode() * 393857768; } @Override public String toString() { return String.format( "GoalThreeBlocks{x=%s,y=%s,z=%s}", SettingsUtil.maybeCensor(x), SettingsUtil.maybeCensor(y), SettingsUtil.maybeCensor(z) ); } } public List<BlockPos> droppedItemsScan() { if (!Baritone.settings().mineScanDroppedItems.value) { return Collections.emptyList(); } List<BlockPos> ret = new ArrayList<>(); for (Entity entity : ((ClientLevel) ctx.world()).entitiesForRendering()) { if (entity instanceof ItemEntity) { ItemEntity ei = (ItemEntity) entity; if (filter.has(ei.getItem())) { ret.add(entity.blockPosition()); } } } ret.addAll(anticipatedDrops.keySet()); return ret; } public static List<BlockPos> searchWorld(CalculationContext ctx, BlockOptionalMetaLookup filter, int max, List<BlockPos> alreadyKnown, List<BlockPos> blacklist, List<BlockPos> dropped) { List<BlockPos> locs = new ArrayList<>(); List<Block> untracked = new ArrayList<>(); for (BlockOptionalMeta bom : filter.blocks()) { Block block = bom.getBlock(); if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(block)) { BetterBlockPos pf = ctx.baritone.getPlayerContext().playerFeet(); // maxRegionDistanceSq 2 means adjacent directly or adjacent diagonally; nothing further than that locs.addAll(ctx.worldData.getCachedWorld().getLocationsOf( BlockUtils.blockToString(block), Baritone.settings().maxCachedWorldScanCount.value, pf.x, pf.z, 2 )); } else { untracked.add(block); } } locs = prune(ctx, locs, filter, max, blacklist, dropped); if (!untracked.isEmpty() || (Baritone.settings().extendCacheOnThreshold.value && locs.size() < max)) { locs.addAll(BaritoneAPI.getProvider().getWorldScanner().scanChunkRadius( ctx.getBaritone().getPlayerContext(), filter, max, 10, 32 )); // maxSearchRadius is NOT sq } locs.addAll(alreadyKnown); return prune(ctx, locs, filter, max, blacklist, dropped); } private boolean addNearby() { List<BlockPos> dropped = droppedItemsScan(); knownOreLocations.addAll(dropped); BlockPos playerFeet = ctx.playerFeet(); BlockStateInterface bsi = new BlockStateInterface(ctx); BlockOptionalMetaLookup filter = filterFilter(); if (filter == null) { return false; } int searchDist = 10; double fakedBlockReachDistance = 20; // at least 10 * sqrt(3) with some extra space to account for positioning within the block for (int x = playerFeet.getX() - searchDist; x <= playerFeet.getX() + searchDist; x++) { for (int y = playerFeet.getY() - searchDist; y <= playerFeet.getY() + searchDist; y++) { for (int z = playerFeet.getZ() - searchDist; z <= playerFeet.getZ() + searchDist; z++) { // crucial to only add blocks we can see because otherwise this // is an x-ray and it'll get caught if (filter.has(bsi.get0(x, y, z))) { BlockPos pos = new BlockPos(x, y, z); if ((Baritone.settings().legitMineIncludeDiagonals.value && knownOreLocations.stream().anyMatch(ore -> ore.distSqr(pos) <= 2 /* sq means this is pytha dist <= sqrt(2) */)) || RotationUtils.reachable(ctx, pos, fakedBlockReachDistance).isPresent()) { knownOreLocations.add(pos); } } } } } knownOreLocations = prune(new CalculationContext(baritone), knownOreLocations, filter, Baritone.settings().mineMaxOreLocationsCount.value, blacklist, dropped); return true; } private static List<BlockPos> prune(CalculationContext ctx, List<BlockPos> locs2, BlockOptionalMetaLookup filter, int max, List<BlockPos> blacklist, List<BlockPos> dropped) { dropped.removeIf(drop -> { for (BlockPos pos : locs2) { if (pos.distSqr(drop) <= 9 && filter.has(ctx.get(pos.getX(), pos.getY(), pos.getZ())) && MineProcess.plausibleToBreak(ctx, pos)) { // TODO maybe drop also has to be supported? no lava below? return true; } } return false; }); List<BlockPos> locs = locs2 .stream() .distinct() // remove any that are within loaded chunks that aren't actually what we want .filter(pos -> !ctx.bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ()) || filter.has(ctx.get(pos.getX(), pos.getY(), pos.getZ())) || dropped.contains(pos)) // remove any that are implausible to mine (encased in bedrock, or touching lava) .filter(pos -> MineProcess.plausibleToBreak(ctx, pos)) .filter(pos -> { if (Baritone.settings().allowOnlyExposedOres.value) { return isNextToAir(ctx, pos); } else { return true; } }) .filter(pos -> pos.getY() >= Baritone.settings().minYLevelWhileMining.value + ctx.world.dimensionType().minY()) .filter(pos -> pos.getY() <= Baritone.settings().maxYLevelWhileMining.value) .filter(pos -> !blacklist.contains(pos)) .sorted(Comparator.comparingDouble(ctx.getBaritone().getPlayerContext().player().blockPosition()::distSqr)) .collect(Collectors.toList()); if (locs.size() > max) { return locs.subList(0, max); } return locs; } public static boolean isNextToAir(CalculationContext ctx, BlockPos pos) { int radius = Baritone.settings().allowOnlyExposedOresDistance.value; for (int dx = -radius; dx <= radius; dx++) { for (int dy = -radius; dy <= radius; dy++) { for (int dz = -radius; dz <= radius; dz++) { if (Math.abs(dx) + Math.abs(dy) + Math.abs(dz) <= radius && MovementHelper.isTransparent(ctx.getBlock(pos.getX() + dx, pos.getY() + dy, pos.getZ() + dz))) { return true; } } } } return false; } public static boolean plausibleToBreak(CalculationContext ctx, BlockPos pos) { BlockState state = ctx.bsi.get0(pos); if (MovementHelper.getMiningDurationTicks(ctx, pos.getX(), pos.getY(), pos.getZ(), state, true) >= COST_INF) { return false; } if (MovementHelper.avoidBreaking(ctx.bsi, pos.getX(), pos.getY(), pos.getZ(), state)) { return false; } // bedrock above and below makes it implausible, otherwise we're good return !(ctx.bsi.get0(pos.above()).getBlock() == Blocks.BEDROCK && ctx.bsi.get0(pos.below()).getBlock() == Blocks.BEDROCK); } @Override public void mineByName(int quantity, String... blocks) { mine(quantity, new BlockOptionalMetaLookup(blocks)); } @Override public void mine(int quantity, BlockOptionalMetaLookup filter) { this.filter = filter; if (this.filterFilter() == null) { this.filter = null; } this.desiredQuantity = quantity; this.knownOreLocations = new ArrayList<>(); this.blacklist = new ArrayList<>(); this.branchPoint = null; this.branchPointRunaway = null; this.anticipatedDrops = new HashMap<>(); if (filter != null) { rescan(new ArrayList<>(), new CalculationContext(baritone)); } } private BlockOptionalMetaLookup filterFilter() { if (this.filter == null) { return null; } if (!Baritone.settings().allowBreak.value) { BlockOptionalMetaLookup f = new BlockOptionalMetaLookup(this.filter.blocks() .stream() .filter(e -> Baritone.settings().allowBreakAnyway.value.contains(e.getBlock())) .toArray(BlockOptionalMeta[]::new)); if (f.blocks().isEmpty()) { logDirect("Unable to mine when allowBreak is false and target block is not in allowBreakAnyway!"); return null; } return f; } return filter; } } ================================================ FILE: src/main/java/baritone/process/elytra/BlockStateOctreeInterface.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process.elytra; import dev.babbaj.pathfinder.NetherPathfinder; import dev.babbaj.pathfinder.Octree; /** * @author Brady */ public final class BlockStateOctreeInterface { private final NetherPathfinderContext context; private final long contextPtr; transient long chunkPtr; // Guarantee that the first lookup will fetch the context by setting MAX_VALUE private int prevChunkX = Integer.MAX_VALUE; private int prevChunkZ = Integer.MAX_VALUE; public BlockStateOctreeInterface(final NetherPathfinderContext context) { this.context = context; this.contextPtr = context.context; } public boolean get0(final int x, final int y, final int z) { if ((y | (127 - y)) < 0) { return false; } final int chunkX = x >> 4; final int chunkZ = z >> 4; if (this.chunkPtr == 0 | ((chunkX ^ this.prevChunkX) | (chunkZ ^ this.prevChunkZ)) != 0) { this.prevChunkX = chunkX; this.prevChunkZ = chunkZ; this.chunkPtr = NetherPathfinder.getOrCreateChunk(this.contextPtr, chunkX, chunkZ); } return Octree.getBlock(this.chunkPtr, x & 0xF, y & 0x7F, z & 0xF); } } ================================================ FILE: src/main/java/baritone/process/elytra/ElytraBehavior.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process.elytra; import baritone.Baritone; import baritone.api.Settings; import baritone.api.behavior.look.IAimProcessor; import baritone.api.behavior.look.ITickableAimProcessor; import baritone.api.event.events.*; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.process.ElytraProcess; import baritone.utils.BlockStateInterface; import baritone.utils.IRenderer; import baritone.utils.PathRenderer; import baritone.utils.accessor.IFireworkRocketEntity; import it.unimi.dsi.fastutil.floats.FloatArrayList; import it.unimi.dsi.fastutil.floats.FloatIterator; import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.inventory.ClickType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.material.Material; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.List; import java.util.Queue; import java.util.*; import java.util.concurrent.*; import java.util.function.UnaryOperator; import static baritone.utils.BaritoneMath.fastCeil; import static baritone.utils.BaritoneMath.fastFloor; public final class ElytraBehavior implements Helper { private final Baritone baritone; private final IPlayerContext ctx; // Render stuff private final List<Pair<Vec3, Vec3>> clearLines; private final List<Pair<Vec3, Vec3>> blockedLines; private List<Vec3> simulationLine; private BlockPos aimPos; private List<BetterBlockPos> visiblePath; // :sunglasses: public final NetherPathfinderContext context; public final PathManager pathManager; private final ElytraProcess process; /** * Remaining cool-down ticks between firework usage */ private int remainingFireworkTicks; /** * Remaining cool-down ticks after the player's position and rotation are reset by the server */ private int remainingSetBackTicks; public boolean landingMode; /** * The most recent minimum number of firework boost ticks, equivalent to {@code 10 * (1 + Flight)} * <p> * Updated every time a firework is automatically used */ private int minimumBoostTicks; private boolean deployedFireworkLastTick; private final int[] nextTickBoostCounter; private BlockStateInterface bsi; private final BlockStateOctreeInterface boi; public final BetterBlockPos destination; private final boolean appendDestination; private final ExecutorService solverExecutor; private Future<Solution> solver; private Solution pendingSolution; private boolean solveNextTick; private long timeLastCacheCull = 0L; // auto swap private int invTickCountdown = 0; private final Queue<Runnable> invTransactionQueue = new LinkedList<>(); public ElytraBehavior(Baritone baritone, ElytraProcess process, BlockPos destination, boolean appendDestination) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); this.clearLines = new CopyOnWriteArrayList<>(); this.blockedLines = new CopyOnWriteArrayList<>(); this.pathManager = this.new PathManager(); this.process = process; this.destination = new BetterBlockPos(destination); this.appendDestination = appendDestination; this.solverExecutor = Executors.newSingleThreadExecutor(); this.nextTickBoostCounter = new int[2]; this.context = new NetherPathfinderContext(Baritone.settings().elytraNetherSeed.value); this.boi = new BlockStateOctreeInterface(context); } public final class PathManager { public NetherPath path; private boolean completePath; private boolean recalculating; private int maxPlayerNear; private int ticksNearUnchanged; private int playerNear; public PathManager() { // lol imagine initializing fields normally this.clear(); } public void tick() { // Recalculate closest path node this.updatePlayerNear(); final int prevMaxNear = this.maxPlayerNear; this.maxPlayerNear = Math.max(this.maxPlayerNear, this.playerNear); if (this.maxPlayerNear == prevMaxNear && ctx.player().isFallFlying()) { this.ticksNearUnchanged++; } else { this.ticksNearUnchanged = 0; } // Obstacles are more important than an incomplete path, handle those first. this.pathfindAroundObstacles(); this.attemptNextSegment(); } public CompletableFuture<Void> pathToDestination() { return this.pathToDestination(ctx.playerFeet()); } public CompletableFuture<Void> pathToDestination(final BlockPos from) { final long start = System.nanoTime(); return this.path0(from, ElytraBehavior.this.destination, UnaryOperator.identity()) .thenRun(() -> { final double distance = this.path.get(0).distanceTo(this.path.get(this.path.size() - 1)); if (this.completePath) { logVerbose(String.format("Computed path (%.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); } else { logVerbose(String.format("Computed segment (Next %.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); } }) .whenComplete((result, ex) -> { this.recalculating = false; if (ex != null) { final Throwable cause = ex.getCause(); if (cause instanceof PathCalculationException) { logDirect("Failed to compute path to destination"); } else { logUnhandledException(cause); } } }); } public CompletableFuture<Void> pathRecalcSegment(final OptionalInt upToIncl) { if (this.recalculating) { throw new IllegalStateException("already recalculating"); } this.recalculating = true; final List<BetterBlockPos> after = upToIncl.isPresent() ? this.path.subList(upToIncl.getAsInt() + 1, this.path.size()) : Collections.emptyList(); final boolean complete = this.completePath; return this.path0(ctx.playerFeet(), upToIncl.isPresent() ? this.path.get(upToIncl.getAsInt()) : ElytraBehavior.this.destination, segment -> segment.append(after.stream(), complete || (segment.isFinished() && !upToIncl.isPresent()))) .whenComplete((result, ex) -> { this.recalculating = false; if (ex != null) { final Throwable cause = ex.getCause(); if (cause instanceof PathCalculationException) { logDirect("Failed to recompute segment"); } else { logUnhandledException(cause); } } }); } public void pathNextSegment(final int afterIncl) { if (this.recalculating) { return; } this.recalculating = true; final List<BetterBlockPos> before = this.path.subList(0, afterIncl + 1); final long start = System.nanoTime(); final BetterBlockPos pathStart = this.path.get(afterIncl); this.path0(pathStart, ElytraBehavior.this.destination, segment -> segment.prepend(before.stream())) .thenRun(() -> { final int recompute = this.path.size() - before.size() - 1; final double distance = this.path.get(0).distanceTo(this.path.get(recompute)); if (this.completePath) { logVerbose(String.format("Computed path (%.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); } else { logVerbose(String.format("Computed segment (Next %.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); } }) .whenComplete((result, ex) -> { this.recalculating = false; if (ex != null) { final Throwable cause = ex.getCause(); if (cause instanceof PathCalculationException) { logDirect("Failed to compute next segment"); if (ctx.player().distanceToSqr(pathStart.getCenter()) < 16 * 16) { logVerbose("Player is near the segment start, therefore repeating this calculation is pointless. Marking as complete"); completePath = true; } } else { logUnhandledException(cause); } } }); } public void clear() { this.path = NetherPath.emptyPath(); this.completePath = true; this.recalculating = false; this.playerNear = 0; this.ticksNearUnchanged = 0; this.maxPlayerNear = 0; } private void setPath(final UnpackedSegment segment) { List<BetterBlockPos> path = segment.collect(); if (ElytraBehavior.this.appendDestination) { BlockPos dest = ElytraBehavior.this.destination; BlockPos last = !path.isEmpty() ? path.get(path.size() - 1) : null; if (last != null && ElytraBehavior.this.clearView(Vec3.atLowerCornerOf(dest), Vec3.atLowerCornerOf(last), false)) { path.add(new BetterBlockPos(dest)); } else { logDirect("unable to land at " + ElytraBehavior.this.destination); process.landingSpotIsBad(new BetterBlockPos(ElytraBehavior.this.destination)); } } this.path = new NetherPath(path); this.completePath = segment.isFinished(); this.playerNear = 0; this.ticksNearUnchanged = 0; this.maxPlayerNear = 0; } public NetherPath getPath() { return this.path; } public int getNear() { return this.playerNear; } // mickey resigned private CompletableFuture<Void> path0(BlockPos src, BlockPos dst, UnaryOperator<UnpackedSegment> operator) { return ElytraBehavior.this.context.pathFindAsync(src, dst) .thenApply(UnpackedSegment::from) .thenApply(operator) .thenAcceptAsync(this::setPath, ctx.minecraft()::execute); } private void pathfindAroundObstacles() { if (this.recalculating) { return; } int rangeStartIncl = playerNear; int rangeEndExcl = playerNear; while (rangeEndExcl < path.size() && context.hasChunk(new ChunkPos(path.get(rangeEndExcl)))) { rangeEndExcl++; } // rangeEndExcl now represents an index either not in the path, or just outside render distance if (rangeStartIncl >= rangeEndExcl) { // not loaded yet? return; } final BetterBlockPos rangeStart = path.get(rangeStartIncl); if (!ElytraBehavior.this.passable(rangeStart.x, rangeStart.y, rangeStart.z, false)) { // we're in a wall return; // previous iterations of this function SHOULD have fixed this by now :rage_cat: } if (ElytraBehavior.this.process.state != ElytraProcess.State.LANDING && this.ticksNearUnchanged > 100) { this.pathRecalcSegment(OptionalInt.of(rangeEndExcl - 1)) .thenRun(() -> { logVerbose("Recalculating segment, no progress in last 100 ticks"); }); this.ticksNearUnchanged = 0; return; } boolean canSeeAny = false; for (int i = rangeStartIncl; i < rangeEndExcl - 1; i++) { if (ElytraBehavior.this.clearView(ctx.playerFeetAsVec(), this.path.getVec(i), false) || ElytraBehavior.this.clearView(ctx.playerHead(), this.path.getVec(i), false)) { canSeeAny = true; } if (!ElytraBehavior.this.clearView(this.path.getVec(i), this.path.getVec(i + 1), false)) { // obstacle. where do we return to pathing? // if the end of render distance is closer to goal, then that's fine, otherwise we'd be "digging our hole deeper" and making an already bad backtrack worse OptionalInt rejoinMainPathAt; if (this.path.get(rangeEndExcl - 1).distanceSq(ElytraBehavior.this.destination) < ctx.playerFeet().distanceSq(ElytraBehavior.this.destination)) { rejoinMainPathAt = OptionalInt.of(rangeEndExcl - 1); // rejoin after current render distance } else { rejoinMainPathAt = OptionalInt.empty(); // large backtrack detected. ignore render distance, rejoin later on } final BetterBlockPos blockage = this.path.get(i); final double distance = ctx.playerFeet().distanceTo(this.path.get(rejoinMainPathAt.orElse(path.size() - 1))); final long start = System.nanoTime(); this.pathRecalcSegment(rejoinMainPathAt) .thenRun(() -> { logVerbose(String.format("Recalculated segment around path blockage near %s %s %s (next %.1f blocks in %.4f seconds)", SettingsUtil.maybeCensor(blockage.x), SettingsUtil.maybeCensor(blockage.y), SettingsUtil.maybeCensor(blockage.z), distance, (System.nanoTime() - start) / 1e9d )); }); return; } } if (!canSeeAny && rangeStartIncl < rangeEndExcl - 2 && process.state != ElytraProcess.State.GET_TO_JUMP) { this.pathRecalcSegment(OptionalInt.of(rangeEndExcl - 1)).thenRun(() -> logVerbose("Recalculated segment since no path points were visible")); } } private void attemptNextSegment() { if (this.recalculating) { return; } final int last = this.path.size() - 1; if (!this.completePath && ctx.world().isLoaded(this.path.get(last))) { this.pathNextSegment(last); } } public void updatePlayerNear() { if (this.path.isEmpty()) { return; } int index = this.playerNear; final BetterBlockPos pos = ctx.playerFeet(); for (int i = index; i >= Math.max(index - 1000, 0); i -= 10) { if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { index = i; // intentional: this changes the bound of the loop } } for (int i = index; i < Math.min(index + 1000, path.size()); i += 10) { if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { index = i; // intentional: this changes the bound of the loop } } for (int i = index; i >= Math.max(index - 50, 0); i--) { if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { index = i; // intentional: this changes the bound of the loop } } for (int i = index; i < Math.min(index + 50, path.size()); i++) { if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { index = i; // intentional: this changes the bound of the loop } } this.playerNear = index; } public boolean isComplete() { return this.completePath; } } public void onRenderPass(RenderEvent event) { final Settings settings = Baritone.settings(); if (this.visiblePath != null) { PathRenderer.drawPath(event.getModelViewStack(), this.visiblePath, 0, Color.RED, false, 0, 0, 0.0D); } if (this.aimPos != null) { PathRenderer.drawGoal(event.getModelViewStack(), ctx, new GoalBlock(this.aimPos), event.getPartialTicks(), Color.GREEN); } if (!this.clearLines.isEmpty() && settings.elytraRenderRaytraces.value) { IRenderer.startLines(Color.GREEN, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); for (Pair<Vec3, Vec3> line : this.clearLines) { IRenderer.emitLine(event.getModelViewStack(), line.first(), line.second()); } IRenderer.endLines(settings.renderPathIgnoreDepth.value); } if (!this.blockedLines.isEmpty() && Baritone.settings().elytraRenderRaytraces.value) { IRenderer.startLines(Color.BLUE, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); for (Pair<Vec3, Vec3> line : this.blockedLines) { IRenderer.emitLine(event.getModelViewStack(), line.first(), line.second()); } IRenderer.endLines(settings.renderPathIgnoreDepth.value); } if (this.simulationLine != null && Baritone.settings().elytraRenderSimulation.value) { IRenderer.startLines(new Color(0x36CCDC), settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); final Vec3 offset = ctx.player().getPosition(event.getPartialTicks()); for (int i = 0; i < this.simulationLine.size() - 1; i++) { final Vec3 src = this.simulationLine.get(i).add(offset); final Vec3 dst = this.simulationLine.get(i + 1).add(offset); IRenderer.emitLine(event.getModelViewStack(), src, dst); } IRenderer.endLines(settings.renderPathIgnoreDepth.value); } } public void onChunkEvent(ChunkEvent event) { if (event.isPostPopulate() && this.context != null) { final LevelChunk chunk = ctx.world().getChunk(event.getX(), event.getZ()); this.context.queueForPacking(chunk); } } public void onBlockChange(BlockChangeEvent event) { this.context.queueBlockUpdate(event); } public void onReceivePacket(PacketEvent event) { if (event.getPacket() instanceof ClientboundPlayerPositionPacket) { ctx.minecraft().execute(() -> { this.remainingSetBackTicks = Baritone.settings().elytraFireworkSetbackUseDelay.value; }); } } public void pathTo() { if (!Baritone.settings().elytraAutoJump.value || ctx.player().isFallFlying()) { this.pathManager.pathToDestination(); } } public void destroy() { if (this.solver != null) { this.solver.cancel(true); } this.solverExecutor.shutdown(); try { while (!this.solverExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) {} } catch (InterruptedException e) { e.printStackTrace(); } this.context.destroy(); } public void repackChunks() { ChunkSource chunkProvider = ctx.world().getChunkSource(); BetterBlockPos playerPos = ctx.playerFeet(); int playerChunkX = playerPos.getX() >> 4; int playerChunkZ = playerPos.getZ() >> 4; int minX = playerChunkX - 40; int minZ = playerChunkZ - 40; int maxX = playerChunkX + 40; int maxZ = playerChunkZ + 40; for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { LevelChunk chunk = chunkProvider.getChunk(x, z, false); if (chunk != null && !chunk.isEmpty()) { this.context.queueForPacking(chunk); } } } } public void onTick() { synchronized (this.context.cullingLock) { this.onTick0(); } final long now = System.currentTimeMillis(); if ((now - this.timeLastCacheCull) / 1000 > Baritone.settings().elytraTimeBetweenCacheCullSecs.value) { this.context.queueCacheCulling(ctx.player().chunkPosition().x, ctx.player().chunkPosition().z, Baritone.settings().elytraCacheCullDistance.value, this.boi); this.timeLastCacheCull = now; } } private void onTick0() { // Fetch the previous solution, regardless of if it's going to be used this.pendingSolution = null; if (this.solver != null) { try { this.pendingSolution = this.solver.get(); } catch (Exception ignored) { // it doesn't matter if get() fails since the solution can just be recalculated synchronously } finally { this.solver = null; } } tickInventoryTransactions(); // Certified mojang employee incident if (this.remainingFireworkTicks > 0) { this.remainingFireworkTicks--; } if (this.remainingSetBackTicks > 0) { this.remainingSetBackTicks--; } if (!this.getAttachedFirework().isPresent()) { this.minimumBoostTicks = 0; } // Reset rendered elements this.clearLines.clear(); this.blockedLines.clear(); this.visiblePath = null; this.simulationLine = null; this.aimPos = null; final List<BetterBlockPos> path = this.pathManager.getPath(); if (path.isEmpty()) { return; } else if (this.destination == null) { this.pathManager.clear(); return; } // ctx AND context???? :DDD this.bsi = new BlockStateInterface(ctx); this.pathManager.tick(); final int playerNear = this.pathManager.getNear(); this.visiblePath = path.subList( Math.max(playerNear - 30, 0), Math.min(playerNear + 100, path.size()) ); } /** * Called by {@link baritone.process.ElytraProcess#onTick(boolean, boolean)} when the process is in control and the player is flying */ public void tick() { if (this.pathManager.getPath().isEmpty()) { return; } trySwapElytra(); if (ctx.player().horizontalCollision) { logVerbose("hbonk"); } if (ctx.player().verticalCollision) { logVerbose("vbonk"); } final SolverContext solverContext = this.new SolverContext(false); this.solveNextTick = true; // If there's no previously calculated solution to use, or the context used at the end of last tick doesn't match this tick final Solution solution; if (this.pendingSolution == null || !this.pendingSolution.context.equals(solverContext)) { solution = this.solveAngles(solverContext); } else { solution = this.pendingSolution; } if (this.deployedFireworkLastTick) { this.nextTickBoostCounter[solverContext.boost.isBoosted() ? 1 : 0]++; this.deployedFireworkLastTick = false; } final boolean inLava = ctx.player().isInLava(); if (inLava) { baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); } if (solution == null) { logVerbose("no solution"); return; } baritone.getLookBehavior().updateTarget(solution.rotation, false); if (!solution.solvedPitch) { logVerbose("no pitch solution, probably gonna crash in a few ticks LOL!!!"); return; } else { this.aimPos = new BetterBlockPos(solution.goingTo.x, solution.goingTo.y, solution.goingTo.z); } this.tickUseFireworks( solution.context.start, solution.goingTo, solution.context.boost.isBoosted(), solution.forceUseFirework || inLava ); } public void onPostTick(TickEvent event) { if (event.getType() == TickEvent.Type.IN && this.solveNextTick) { // We're at the end of the tick, the player's position likely updated and the closest path node could've // changed. Updating it now will avoid unnecessary recalculation on the main thread. this.pathManager.updatePlayerNear(); final SolverContext context = this.new SolverContext(true); this.solver = this.solverExecutor.submit(() -> this.solveAngles(context)); this.solveNextTick = false; } } private Solution solveAngles(final SolverContext context) { final NetherPath path = context.path; final int playerNear = landingMode ? path.size() - 1 : context.playerNear; final Vec3 start = context.start; Solution solution = null; for (int relaxation = 0; relaxation < 3; relaxation++) { // try for a strict solution first, then relax more and more (if we're in a corner or near some blocks, it will have to relax its constraints a bit) int[] heights = context.boost.isBoosted() ? new int[]{20, 10, 5, 0} : new int[]{0}; // attempt to gain height, if we can, so as not to waste the boost int lookahead = relaxation == 0 ? 2 : 3; // ideally this would be expressed as a distance in blocks, rather than a number of voxel steps //int minStep = Math.max(0, playerNear - relaxation); int minStep = playerNear; for (int i = Math.min(playerNear + 20, path.size() - 1); i >= minStep; i--) { final List<Pair<Vec3, Integer>> candidates = new ArrayList<>(); for (int dy : heights) { if (relaxation == 0 || i == minStep) { // no interp candidates.add(new Pair<>(path.getVec(i), dy)); } else if (relaxation == 1) { final double[] interps = new double[]{1.0, 0.75, 0.5, 0.25}; for (double interp : interps) { final Vec3 dest = interp == 1.0 ? path.getVec(i) : path.getVec(i).scale(interp).add(path.getVec(i - 1).scale(1.0 - interp)); candidates.add(new Pair<>(dest, dy)); } } else { // Create a point along the segment every block final Vec3 delta = path.getVec(i).subtract(path.getVec(i - 1)); final int steps = fastFloor(delta.length()); final Vec3 step = delta.normalize(); Vec3 stepped = path.getVec(i); for (int interp = 0; interp < steps; interp++) { candidates.add(new Pair<>(stepped, dy)); stepped = stepped.subtract(step); } } } for (final Pair<Vec3, Integer> candidate : candidates) { final Integer augment = candidate.second(); Vec3 dest = candidate.first().add(0, augment, 0); if (landingMode) { dest = dest.add(0.5, 0.5, 0.5); } if (augment != 0) { if (i + lookahead >= path.size()) { continue; } if (start.distanceTo(dest) < 40) { if (!this.clearView(dest, path.getVec(i + lookahead).add(0, augment, 0), false) || !this.clearView(dest, path.getVec(i + lookahead), false)) { // aka: don't go upwards if doing so would prevent us from being able to see the next position **OR** the modified next position continue; } } else { // but if it's far away, allow gaining altitude if we could lose it again by the time we get there if (!this.clearView(dest, path.getVec(i), false)) { continue; } } } final double minAvoidance = Baritone.settings().elytraMinimumAvoidance.value; final Double growth = relaxation == 2 ? null : relaxation == 0 ? 2 * minAvoidance : minAvoidance; if (this.isHitboxClear(context, dest, growth)) { // Yaw is trivial, just calculate the rotation required to face the destination final float yaw = RotationUtils.calcRotationFromVec3d(start, dest, ctx.playerRotations()).getYaw(); final Pair<Float, Boolean> pitch = this.solvePitch(context, dest, relaxation); if (pitch == null) { solution = new Solution(context, new Rotation(yaw, ctx.playerRotations().getPitch()), null, false, false); continue; } // A solution was found with yaw AND pitch, so just immediately return it. return new Solution(context, new Rotation(yaw, pitch.first()), dest, true, pitch.second()); } } } } return solution; } private void tickUseFireworks(final Vec3 start, final Vec3 goingTo, final boolean isBoosted, final boolean forceUseFirework) { if (this.remainingSetBackTicks > 0) { logDebug("waiting for elytraFireworkSetbackUseDelay: " + this.remainingSetBackTicks); return; } if (this.landingMode) { return; } final boolean useOnDescend = !Baritone.settings().elytraConserveFireworks.value || ctx.player().position().y < goingTo.y + 5; final double currentSpeed = new Vec3( ctx.player().getDeltaMovement().x, // ignore y component if we are BOTH below where we want to be AND descending ctx.player().position().y < goingTo.y ? Math.max(0, ctx.player().getDeltaMovement().y) : ctx.player().getDeltaMovement().y, ctx.player().getDeltaMovement().z ).lengthSqr(); final double elytraFireworkSpeed = Baritone.settings().elytraFireworkSpeed.value; if (this.remainingFireworkTicks <= 0 && (forceUseFirework || (!isBoosted && useOnDescend && (ctx.player().position().y < goingTo.y - 5 || start.distanceTo(new Vec3(goingTo.x + 0.5, ctx.player().position().y, goingTo.z + 0.5)) > 5) // UGH!!!!!!! && currentSpeed < elytraFireworkSpeed * elytraFireworkSpeed)) ) { // Prioritize boosting fireworks over regular ones // TODO: Take the minimum boost time into account? if (!baritone.getInventoryBehavior().throwaway(true, ElytraBehavior::isBoostingFireworks) && !baritone.getInventoryBehavior().throwaway(true, ElytraBehavior::isFireworks)) { logDirect("no fireworks"); return; } logVerbose("attempting to use firework" + (forceUseFirework ? " (forced)" : "")); ctx.playerController().processRightClick(ctx.player(), ctx.world(), InteractionHand.MAIN_HAND); this.minimumBoostTicks = 10 * (1 + getFireworkBoost(ctx.player().getItemInHand(InteractionHand.MAIN_HAND)).orElse(0)); this.remainingFireworkTicks = 10; this.deployedFireworkLastTick = true; } } private final class SolverContext { public final NetherPath path; public final int playerNear; public final Vec3 start; public final Vec3 motion; public final AABB boundingBox; public final boolean ignoreLava; public final FireworkBoost boost; public final IAimProcessor aimProcessor; /** * Creates a new SolverContext using the current state of the path, player, and firework boost at the time of * construction. * * @param async Whether the computation is being done asynchronously at the end of a game tick. */ public SolverContext(boolean async) { this.path = ElytraBehavior.this.pathManager.getPath(); this.playerNear = ElytraBehavior.this.pathManager.getNear(); this.start = ctx.playerFeetAsVec(); this.motion = ctx.playerMotion(); this.boundingBox = ctx.player().getBoundingBox(); this.ignoreLava = ctx.player().isInLava(); final Integer fireworkTicksExisted; if (async && ElytraBehavior.this.deployedFireworkLastTick) { final int[] counter = ElytraBehavior.this.nextTickBoostCounter; fireworkTicksExisted = counter[1] > counter[0] ? 0 : null; } else { fireworkTicksExisted = ElytraBehavior.this.getAttachedFirework().map(e -> e.tickCount).orElse(null); } this.boost = new FireworkBoost(fireworkTicksExisted, ElytraBehavior.this.minimumBoostTicks); ITickableAimProcessor aim = ElytraBehavior.this.baritone.getLookBehavior().getAimProcessor().fork(); if (async) { // async computation is done at the end of a tick, advance by 1 to prepare for the next tick aim.advance(1); } this.aimProcessor = aim; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || o.getClass() != SolverContext.class) { return false; } SolverContext other = (SolverContext) o; return this.path == other.path // Contents aren't modified, just compare by reference && this.playerNear == other.playerNear && Objects.equals(this.start, other.start) && Objects.equals(this.motion, other.motion) && Objects.equals(this.boundingBox, other.boundingBox) && this.ignoreLava == other.ignoreLava && Objects.equals(this.boost, other.boost); } } private static final class FireworkBoost { private final Integer fireworkTicksExisted; private final int minimumBoostTicks; private final int maximumBoostTicks; /** * @param fireworkTicksExisted The ticksExisted of the attached firework entity, or {@code null} if no entity. * @param minimumBoostTicks The minimum number of boost ticks that the attached firework entity, if any, will * provide. */ public FireworkBoost(final Integer fireworkTicksExisted, final int minimumBoostTicks) { this.fireworkTicksExisted = fireworkTicksExisted; // this.lifetime = 10 * i + this.rand.nextInt(6) + this.rand.nextInt(7); this.minimumBoostTicks = minimumBoostTicks; this.maximumBoostTicks = minimumBoostTicks + 11; } public boolean isBoosted() { return this.fireworkTicksExisted != null; } /** * @return The guaranteed number of remaining ticks with boost */ public int getGuaranteedBoostTicks() { return this.isBoosted() ? Math.max(0, this.minimumBoostTicks - this.fireworkTicksExisted) : 0; } /** * @return The maximum number of remaining ticks with boost */ public int getMaximumBoostTicks() { return this.isBoosted() ? Math.max(0, this.maximumBoostTicks - this.fireworkTicksExisted) : 0; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || o.getClass() != FireworkBoost.class) { return false; } FireworkBoost other = (FireworkBoost) o; if (!this.isBoosted() && !other.isBoosted()) { return true; } return Objects.equals(this.fireworkTicksExisted, other.fireworkTicksExisted) && this.minimumBoostTicks == other.minimumBoostTicks && this.maximumBoostTicks == other.maximumBoostTicks; } } private static final class PitchResult { public final float pitch; public final double dot; public final List<Vec3> steps; public PitchResult(float pitch, double dot, List<Vec3> steps) { this.pitch = pitch; this.dot = dot; this.steps = steps; } } private static final class Solution { public final SolverContext context; public final Rotation rotation; public final Vec3 goingTo; public final boolean solvedPitch; public final boolean forceUseFirework; public Solution(SolverContext context, Rotation rotation, Vec3 goingTo, boolean solvedPitch, boolean forceUseFirework) { this.context = context; this.rotation = rotation; this.goingTo = goingTo; this.solvedPitch = solvedPitch; this.forceUseFirework = forceUseFirework; } } public static boolean isFireworks(final ItemStack itemStack) { if (itemStack.getItem() != Items.FIREWORK_ROCKET) { return false; } // If it has NBT data, make sure it won't cause us to explode. final CompoundTag compound = itemStack.getTagElement("Fireworks"); return compound == null || !compound.getAllKeys().contains("Explosions"); } private static boolean isBoostingFireworks(final ItemStack itemStack) { return getFireworkBoost(itemStack).isPresent(); } private static OptionalInt getFireworkBoost(final ItemStack itemStack) { if (isFireworks(itemStack)) { final CompoundTag compound = itemStack.getTagElement("Fireworks"); if (compound != null && compound.getAllKeys().contains("Flight")) { return OptionalInt.of(compound.getByte("Flight")); } } return OptionalInt.empty(); } private Optional<FireworkRocketEntity> getAttachedFirework() { return ctx.entitiesStream() .filter(x -> x instanceof FireworkRocketEntity) .filter(x -> Objects.equals(((IFireworkRocketEntity) x).getBoostedEntity(), ctx.player())) .map(x -> (FireworkRocketEntity) x) .findFirst(); } private boolean isHitboxClear(final SolverContext context, final Vec3 dest, final Double growAmount) { final Vec3 start = context.start; final boolean ignoreLava = context.ignoreLava; if (!this.clearView(start, dest, ignoreLava)) { return false; } if (growAmount == null) { return true; } final AABB bb = context.boundingBox.inflate(growAmount); final double ox = dest.x - start.x; final double oy = dest.y - start.y; final double oz = dest.z - start.z; final double[] src = new double[]{ bb.minX, bb.minY, bb.minZ, bb.minX, bb.minY, bb.maxZ, bb.minX, bb.maxY, bb.minZ, bb.minX, bb.maxY, bb.maxZ, bb.maxX, bb.minY, bb.minZ, bb.maxX, bb.minY, bb.maxZ, bb.maxX, bb.maxY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, }; final double[] dst = new double[]{ bb.minX + ox, bb.minY + oy, bb.minZ + oz, bb.minX + ox, bb.minY + oy, bb.maxZ + oz, bb.minX + ox, bb.maxY + oy, bb.minZ + oz, bb.minX + ox, bb.maxY + oy, bb.maxZ + oz, bb.maxX + ox, bb.minY + oy, bb.minZ + oz, bb.maxX + ox, bb.minY + oy, bb.maxZ + oz, bb.maxX + ox, bb.maxY + oy, bb.minZ + oz, bb.maxX + ox, bb.maxY + oy, bb.maxZ + oz, }; // Use non-batching method without early failure if (Baritone.settings().elytraRenderHitboxRaytraces.value) { boolean clear = true; for (int i = 0; i < 8; i++) { final Vec3 s = new Vec3(src[i * 3], src[i * 3 + 1], src[i * 3 + 2]); final Vec3 d = new Vec3(dst[i * 3], dst[i * 3 + 1], dst[i * 3 + 2]); // Don't forward ignoreLava since the batch call doesn't care about it if (!this.clearView(s, d, false)) { clear = false; } } return clear; } return this.context.raytrace(8, src, dst, NetherPathfinderContext.Visibility.ALL); } public boolean clearView(Vec3 start, Vec3 dest, boolean ignoreLava) { final boolean clear; if (!ignoreLava) { // if start == dest then the cpp raytracer dies clear = start.equals(dest) || this.context.raytrace(start, dest); } else { clear = ctx.world().clip(new ClipContext(start, dest, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, ctx.player())).getType() == HitResult.Type.MISS; } if (Baritone.settings().elytraRenderRaytraces.value) { (clear ? this.clearLines : this.blockedLines).add(new Pair<>(start, dest)); } return clear; } private static FloatArrayList pitchesToSolveFor(final float goodPitch, final boolean desperate) { final float minPitch = desperate ? -90 : Math.max(goodPitch - Baritone.settings().elytraPitchRange.value, -89); final float maxPitch = desperate ? 90 : Math.min(goodPitch + Baritone.settings().elytraPitchRange.value, 89); final FloatArrayList pitchValues = new FloatArrayList(fastCeil(maxPitch - minPitch) + 1); for (float pitch = goodPitch; pitch <= maxPitch; pitch++) { pitchValues.add(pitch); } for (float pitch = goodPitch - 1; pitch >= minPitch; pitch--) { pitchValues.add(pitch); } return pitchValues; } @FunctionalInterface private interface IntTriFunction<T> { T apply(int first, int second, int third); } private static final class IntTriple { public final int first; public final int second; public final int third; public IntTriple(int first, int second, int third) { this.first = first; this.second = second; this.third = third; } } private Pair<Float, Boolean> solvePitch(final SolverContext context, final Vec3 goal, final int relaxation) { final boolean desperate = relaxation == 2; final float goodPitch = RotationUtils.calcRotationFromVec3d(context.start, goal, ctx.playerRotations()).getPitch(); final FloatArrayList pitches = pitchesToSolveFor(goodPitch, desperate); final IntTriFunction<PitchResult> solve = (ticks, ticksBoosted, ticksBoostDelay) -> this.solvePitch(context, goal, relaxation, pitches.iterator(), ticks, ticksBoosted, ticksBoostDelay); final List<IntTriple> tests = new ArrayList<>(); if (context.boost.isBoosted()) { final int guaranteed = context.boost.getGuaranteedBoostTicks(); if (guaranteed == 0) { // uncertain when boost will run out final int lookahead = Math.max(4, 10 - context.boost.getMaximumBoostTicks()); tests.add(new IntTriple(lookahead, 1, 0)); } else if (guaranteed <= 5) { // boost will run out within 5 ticks tests.add(new IntTriple(guaranteed + 5, guaranteed, 0)); } else { // there's plenty of guaranteed boost tests.add(new IntTriple(guaranteed + 1, guaranteed, 0)); } } // Standard test, assume (not) boosted for entire duration final int ticks = desperate ? 3 : context.boost.isBoosted() ? Math.max(5, context.boost.getGuaranteedBoostTicks()) : Baritone.settings().elytraSimulationTicks.value; tests.add(new IntTriple(ticks, context.boost.isBoosted() ? ticks : 0, 0)); final Optional<PitchResult> result = tests.stream() .map(i -> solve.apply(i.first, i.second, i.third)) .filter(Objects::nonNull) .findFirst(); if (result.isPresent()) { return new Pair<>(result.get().pitch, false); } // If we used a firework would we be able to get out of the current situation??? perhaps if (desperate) { final List<IntTriple> testsBoost = new ArrayList<>(); testsBoost.add(new IntTriple(ticks, 10, 3)); testsBoost.add(new IntTriple(ticks, 10, 2)); testsBoost.add(new IntTriple(ticks, 10, 1)); final Optional<PitchResult> resultBoost = testsBoost.stream() .map(i -> solve.apply(i.first, i.second, i.third)) .filter(Objects::nonNull) .findFirst(); if (resultBoost.isPresent()) { return new Pair<>(resultBoost.get().pitch, true); } } return null; } private PitchResult solvePitch(final SolverContext context, final Vec3 goal, final int relaxation, final FloatIterator pitches, final int ticks, final int ticksBoosted, final int ticksBoostDelay) { // we are at a certain velocity, but we have a target velocity // what pitch would get us closest to our target velocity? // yaw is easy so we only care about pitch final Vec3 goalDelta = goal.subtract(context.start); final Vec3 goalDirection = goalDelta.normalize(); final Deque<PitchResult> bestResults = new ArrayDeque<>(); while (pitches.hasNext()) { final float pitch = pitches.nextFloat(); final List<Vec3> displacement = this.simulate( context, goalDelta, pitch, ticks, ticksBoosted, ticksBoostDelay ); if (displacement == null) { continue; } final Vec3 last = displacement.get(displacement.size() - 1); double goodness = goalDirection.dot(last.normalize()); if (landingMode) { goodness = -goalDelta.subtract(last).length(); } final PitchResult bestSoFar = bestResults.peek(); if (bestSoFar == null || goodness > bestSoFar.dot) { bestResults.push(new PitchResult(pitch, goodness, displacement)); } } outer: for (final PitchResult result : bestResults) { if (relaxation < 2) { // Ensure that the goal is visible along the entire simulated path // Reverse order iteration since the last position is most likely to fail for (int i = result.steps.size() - 1; i >= 1; i--) { if (!clearView(context.start.add(result.steps.get(i)), goal, context.ignoreLava)) { continue outer; } } } else { // Ensure that the goal is visible from the final position if (!clearView(context.start.add(result.steps.get(result.steps.size() - 1)), goal, context.ignoreLava)) { continue; } } this.simulationLine = result.steps; return result; } return null; } private List<Vec3> simulate(final SolverContext context, final Vec3 goalDelta, final float pitch, final int ticks, final int ticksBoosted, final int ticksBoostDelay) { final ITickableAimProcessor aimProcessor = context.aimProcessor.fork(); Vec3 delta = goalDelta; Vec3 motion = context.motion; AABB hitbox = context.boundingBox; List<Vec3> displacement = new ArrayList<>(ticks + 1); displacement.add(Vec3.ZERO); int remainingTicksBoosted = ticksBoosted; for (int i = 0; i < ticks; i++) { final double cx = hitbox.minX + (hitbox.maxX - hitbox.minX) * 0.5D; final double cz = hitbox.minZ + (hitbox.maxZ - hitbox.minZ) * 0.5D; if (delta.lengthSqr() < 1) { break; } final Rotation rotation = aimProcessor.nextRotation( RotationUtils.calcRotationFromVec3d(Vec3.ZERO, delta, ctx.playerRotations()).withPitch(pitch) ); final Vec3 lookDirection = RotationUtils.calcLookDirectionFromRotation(rotation); motion = step(motion, lookDirection, rotation.getPitch()); delta = delta.subtract(motion); // Collision box while the player is in motion, with additional padding for safety final AABB inMotion = hitbox.inflate(motion.x, motion.y, motion.z).inflate(0.01); int xmin = fastFloor(inMotion.minX); int xmax = fastCeil(inMotion.maxX); int ymin = fastFloor(inMotion.minY); int ymax = fastCeil(inMotion.maxY); int zmin = fastFloor(inMotion.minZ); int zmax = fastCeil(inMotion.maxZ); for (int x = xmin; x < xmax; x++) { for (int y = ymin; y < ymax; y++) { for (int z = zmin; z < zmax; z++) { if (!this.passable(x, y, z, context.ignoreLava)) { return null; } } } } hitbox = hitbox.move(motion); displacement.add(displacement.get(displacement.size() - 1).add(motion)); if (i >= ticksBoostDelay && remainingTicksBoosted-- > 0) { // See EntityFireworkRocket motion = motion.add( lookDirection.x * 0.1 + (lookDirection.x * 1.5 - motion.x) * 0.5, lookDirection.y * 0.1 + (lookDirection.y * 1.5 - motion.y) * 0.5, lookDirection.z * 0.1 + (lookDirection.z * 1.5 - motion.z) * 0.5 ); } } return displacement; } private static Vec3 step(final Vec3 motion, final Vec3 lookDirection, final float pitch) { double motionX = motion.x; double motionY = motion.y; double motionZ = motion.z; float pitchRadians = pitch * RotationUtils.DEG_TO_RAD_F; double pitchBase2 = Math.sqrt(lookDirection.x * lookDirection.x + lookDirection.z * lookDirection.z); double flatMotion = Math.sqrt(motionX * motionX + motionZ * motionZ); double thisIsAlwaysOne = lookDirection.length(); float pitchBase3 = Mth.cos(pitchRadians); //System.out.println("always the same lol " + -pitchBase + " " + pitchBase3); //System.out.println("always the same lol " + Math.abs(pitchBase3) + " " + pitchBase2); //System.out.println("always 1 lol " + thisIsAlwaysOne); pitchBase3 = (float) ((double) pitchBase3 * (double) pitchBase3 * Math.min(1, thisIsAlwaysOne / 0.4)); motionY += -0.08 + (double) pitchBase3 * 0.06; if (motionY < 0 && pitchBase2 > 0) { double speedModifier = motionY * -0.1 * (double) pitchBase3; motionY += speedModifier; motionX += lookDirection.x * speedModifier / pitchBase2; motionZ += lookDirection.z * speedModifier / pitchBase2; } if (pitchRadians < 0) { // if you are looking down (below level) double anotherSpeedModifier = flatMotion * (double) (-Mth.sin(pitchRadians)) * 0.04; motionY += anotherSpeedModifier * 3.2; motionX -= lookDirection.x * anotherSpeedModifier / pitchBase2; motionZ -= lookDirection.z * anotherSpeedModifier / pitchBase2; } if (pitchBase2 > 0) { // this is always true unless you are looking literally straight up (let's just say the bot will never do that) motionX += (lookDirection.x / pitchBase2 * flatMotion - motionX) * 0.1; motionZ += (lookDirection.z / pitchBase2 * flatMotion - motionZ) * 0.1; } motionX *= 0.99f; motionY *= 0.98f; motionZ *= 0.99f; //System.out.println(motionX + " " + motionY + " " + motionZ); return new Vec3(motionX, motionY, motionZ); } private boolean passable(int x, int y, int z, boolean ignoreLava) { if (ignoreLava) { final Material mat = this.bsi.get0(x, y, z).getMaterial(); return mat == Material.AIR || mat == Material.LAVA; } else { return !this.boi.get0(x, y, z); } } private void tickInventoryTransactions() { if (invTickCountdown <= 0) { Runnable r = invTransactionQueue.poll(); if (r != null) { r.run(); invTickCountdown = Baritone.settings().ticksBetweenInventoryMoves.value; } } if (invTickCountdown > 0) invTickCountdown--; } private void queueWindowClick(int windowId, int slotId, int button, ClickType type) { invTransactionQueue.add(() -> ctx.playerController().windowClick(windowId, slotId, button, type, ctx.player())); } private int findGoodElytra() { NonNullList<ItemStack> invy = ctx.player().getInventory().items; for (int i = 0; i < invy.size(); i++) { ItemStack slot = invy.get(i); if (slot.getItem() == Items.ELYTRA && (slot.getItem().getMaxDamage() - slot.getDamageValue()) > Baritone.settings().elytraMinimumDurability.value) { return i; } } return -1; } private void trySwapElytra() { if (!Baritone.settings().elytraAutoSwap.value || !invTransactionQueue.isEmpty()) { return; } ItemStack chest = ctx.player().getItemBySlot(EquipmentSlot.CHEST); if (chest.getItem() != Items.ELYTRA || chest.getItem().getMaxDamage() - chest.getDamageValue() > Baritone.settings().elytraMinimumDurability.value) { return; } int goodElytraSlot = findGoodElytra(); if (goodElytraSlot != -1) { final int CHEST_SLOT = 6; final int slotId = goodElytraSlot < 9 ? goodElytraSlot + 36 : goodElytraSlot; queueWindowClick(ctx.player().inventoryMenu.containerId, slotId, 0, ClickType.PICKUP); queueWindowClick(ctx.player().inventoryMenu.containerId, CHEST_SLOT, 0, ClickType.PICKUP); queueWindowClick(ctx.player().inventoryMenu.containerId, slotId, 0, ClickType.PICKUP); } } void logVerbose(String message) { if (Baritone.settings().elytraChatSpam.value) { logDebug(message); } } } ================================================ FILE: src/main/java/baritone/process/elytra/NetherPath.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process.elytra; import baritone.api.utils.BetterBlockPos; import net.minecraft.world.phys.Vec3; import java.util.AbstractList; import java.util.Collections; import java.util.List; /** * @author Brady */ public final class NetherPath extends AbstractList<BetterBlockPos> { private static final NetherPath EMPTY_PATH = new NetherPath(Collections.emptyList()); private final List<BetterBlockPos> backing; NetherPath(List<BetterBlockPos> backing) { this.backing = backing; } @Override public BetterBlockPos get(int index) { return this.backing.get(index); } @Override public int size() { return this.backing.size(); } /** * @return The last position in the path, or {@code null} if empty */ public BetterBlockPos getLast() { return this.isEmpty() ? null : this.backing.get(this.backing.size() - 1); } public Vec3 getVec(int index) { final BetterBlockPos pos = this.get(index); return new Vec3(pos.x, pos.y, pos.z); } public static NetherPath emptyPath() { return EMPTY_PATH; } } ================================================ FILE: src/main/java/baritone/process/elytra/NetherPathfinderContext.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process.elytra; import baritone.Baritone; import baritone.api.event.events.BlockChangeEvent; import baritone.utils.accessor.IPalettedContainer; import dev.babbaj.pathfinder.NetherPathfinder; import dev.babbaj.pathfinder.Octree; import dev.babbaj.pathfinder.PathSegment; import net.minecraft.core.BlockPos; import net.minecraft.util.BitStorage; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.phys.Vec3; import java.lang.ref.SoftReference; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * @author Brady */ public final class NetherPathfinderContext { private static final BlockState AIR_BLOCK_STATE = Blocks.AIR.defaultBlockState(); // This lock must be held while there are active pointers to chunks in java, // but we just hold it for the entire tick so we don't have to think much about it. public final Object cullingLock = new Object(); // Visible for access in BlockStateOctreeInterface final long context; private final long seed; private final ExecutorService executor; public NetherPathfinderContext(long seed) { this.context = NetherPathfinder.newContext(seed); this.seed = seed; this.executor = Executors.newSingleThreadExecutor(); } public boolean hasChunk(ChunkPos pos) { return NetherPathfinder.hasChunkFromJava(this.context, pos.x, pos.z); } public void queueCacheCulling(int chunkX, int chunkZ, int maxDistanceBlocks, BlockStateOctreeInterface boi) { this.executor.execute(() -> { synchronized (this.cullingLock) { boi.chunkPtr = 0L; NetherPathfinder.cullFarChunks(this.context, chunkX, chunkZ, maxDistanceBlocks); } }); } public void queueForPacking(final LevelChunk chunkIn) { final SoftReference<LevelChunk> ref = new SoftReference<>(chunkIn); this.executor.execute(() -> { // TODO: Prioritize packing recent chunks and/or ones that the path goes through, // and prune the oldest chunks per chunkPackerQueueMaxSize final LevelChunk chunk = ref.get(); if (chunk != null) { long ptr = NetherPathfinder.getOrCreateChunk(this.context, chunk.getPos().x, chunk.getPos().z); writeChunkData(chunk, ptr); } }); } public void queueBlockUpdate(BlockChangeEvent event) { this.executor.execute(() -> { ChunkPos chunkPos = event.getChunkPos(); long ptr = NetherPathfinder.getChunkPointer(this.context, chunkPos.x, chunkPos.z); if (ptr == 0) return; // this shouldn't ever happen event.getBlocks().forEach(pair -> { BlockPos pos = pair.first(); if (pos.getY() >= 128) return; boolean isSolid = pair.second() != AIR_BLOCK_STATE; Octree.setBlock(ptr, pos.getX() & 15, pos.getY(), pos.getZ() & 15, isSolid); }); }); } public CompletableFuture<PathSegment> pathFindAsync(final BlockPos src, final BlockPos dst) { return CompletableFuture.supplyAsync(() -> { final PathSegment segment = NetherPathfinder.pathFind( this.context, src.getX(), src.getY(), src.getZ(), dst.getX(), dst.getY(), dst.getZ(), true, false, 10000, !Baritone.settings().elytraPredictTerrain.value ); if (segment == null) { throw new PathCalculationException("Path calculation failed"); } return segment; }, this.executor); } /** * Performs a raytrace from the given start position to the given end position, returning {@code true} if there is * visibility between the two points. * * @param startX The start X coordinate * @param startY The start Y coordinate * @param startZ The start Z coordinate * @param endX The end X coordinate * @param endY The end Y coordinate * @param endZ The end Z coordinate * @return {@code true} if there is visibility between the points */ public boolean raytrace(final double startX, final double startY, final double startZ, final double endX, final double endY, final double endZ) { return NetherPathfinder.isVisible(this.context, NetherPathfinder.CACHE_MISS_SOLID, startX, startY, startZ, endX, endY, endZ); } /** * Performs a raytrace from the given start position to the given end position, returning {@code true} if there is * visibility between the two points. * * @param start The starting point * @param end The ending point * @return {@code true} if there is visibility between the points */ public boolean raytrace(final Vec3 start, final Vec3 end) { return NetherPathfinder.isVisible(this.context, NetherPathfinder.CACHE_MISS_SOLID, start.x, start.y, start.z, end.x, end.y, end.z); } public boolean raytrace(final int count, final double[] src, final double[] dst, final int visibility) { switch (visibility) { case Visibility.ALL: return NetherPathfinder.isVisibleMulti(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, false) == -1; case Visibility.NONE: return NetherPathfinder.isVisibleMulti(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, true) == -1; case Visibility.ANY: return NetherPathfinder.isVisibleMulti(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, true) != -1; default: throw new IllegalArgumentException("lol"); } } public void raytrace(final int count, final double[] src, final double[] dst, final boolean[] hitsOut, final double[] hitPosOut) { NetherPathfinder.raytrace(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, hitsOut, hitPosOut); } public void cancel() { NetherPathfinder.cancel(this.context); } public void destroy() { this.cancel(); // Ignore anything that was queued up, just shutdown the executor this.executor.shutdownNow(); try { while (!this.executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) {} } catch (InterruptedException e) { e.printStackTrace(); } NetherPathfinder.freeContext(this.context); } public long getSeed() { return this.seed; } private static void writeChunkData(LevelChunk chunk, long ptr) { try { LevelChunkSection[] chunkInternalStorageArray = chunk.getSections(); for (int y0 = 0; y0 < 8; y0++) { final LevelChunkSection extendedblockstorage = chunkInternalStorageArray[y0]; if (extendedblockstorage == null) { continue; } final PalettedContainer<BlockState> bsc = extendedblockstorage.getStates(); final int airId = ((IPalettedContainer<BlockState>) bsc).getPalette().idFor(AIR_BLOCK_STATE); // pasted from FasterWorldScanner final BitStorage array = ((IPalettedContainer<BlockState>) bsc).getStorage(); if (array == null) continue; final long[] longArray = array.getRaw(); final int arraySize = array.getSize(); int bitsPerEntry = array.getBits(); long maxEntryValue = (1L << bitsPerEntry) - 1L; final int yReal = y0 << 4; for (int i = 0, idx = 0; i < longArray.length && idx < arraySize; ++i) { long l = longArray[i]; for (int offset = 0; offset <= (64 - bitsPerEntry) && idx < arraySize; offset += bitsPerEntry, ++idx) { int value = (int) ((l >> offset) & maxEntryValue); int x = (idx & 15); int y = yReal + (idx >> 8); int z = ((idx >> 4) & 15); Octree.setBlock(ptr, x, y, z, value != airId); } } } Octree.setIsFromJava(ptr); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } public static final class Visibility { public static final int ALL = 0; public static final int NONE = 1; public static final int ANY = 2; private Visibility() {} } public static boolean isSupported() { return NetherPathfinder.isThisSystemSupported(); } } ================================================ FILE: src/main/java/baritone/process/elytra/NullElytraProcess.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process.elytra; import baritone.Baritone; import baritone.api.pathing.goals.Goal; import baritone.api.process.IElytraProcess; import baritone.api.process.PathingCommand; import baritone.api.utils.BetterBlockPos; import baritone.utils.BaritoneProcessHelper; import net.minecraft.core.BlockPos; import java.util.Collections; import java.util.List; /** * @author Brady */ public final class NullElytraProcess extends BaritoneProcessHelper implements IElytraProcess { public NullElytraProcess(Baritone baritone) { super(baritone); } @Override public void repackChunks() { throw new UnsupportedOperationException("Called repackChunks() on NullElytraBehavior"); } @Override public BlockPos currentDestination() { return null; } @Override public List<BetterBlockPos> getPath() { return Collections.emptyList(); } @Override public void pathTo(BlockPos destination) { throw new UnsupportedOperationException("Called pathTo() on NullElytraBehavior"); } @Override public void pathTo(Goal destination) { throw new UnsupportedOperationException("Called pathTo() on NullElytraBehavior"); } @Override public void resetState() { } @Override public boolean isActive() { return false; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { throw new UnsupportedOperationException("Called onTick on NullElytraProcess"); } @Override public void onLostControl() { } @Override public String displayName0() { return "NullElytraProcess"; } @Override public boolean isLoaded() { return false; } @Override public boolean isSafeToCancel() { return true; } } ================================================ FILE: src/main/java/baritone/process/elytra/PathCalculationException.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process.elytra; /** * @author Brady */ public final class PathCalculationException extends RuntimeException { public PathCalculationException(final String message) { super(message); } } ================================================ FILE: src/main/java/baritone/process/elytra/UnpackedSegment.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.process.elytra; import baritone.api.utils.BetterBlockPos; import dev.babbaj.pathfinder.PathSegment; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @author Brady */ public final class UnpackedSegment { private final Stream<BetterBlockPos> path; private final boolean finished; public UnpackedSegment(Stream<BetterBlockPos> path, boolean finished) { this.path = path; this.finished = finished; } public UnpackedSegment append(Stream<BetterBlockPos> other, boolean otherFinished) { // The new segment is only finished if the one getting added on is return new UnpackedSegment(Stream.concat(this.path, other), otherFinished); } public UnpackedSegment prepend(Stream<BetterBlockPos> other) { return new UnpackedSegment(Stream.concat(other, this.path), this.finished); } public List<BetterBlockPos> collect() { final List<BetterBlockPos> path = this.path.collect(Collectors.toList()); // Remove backtracks final Map<BetterBlockPos, Integer> positionFirstSeen = new HashMap<>(); for (int i = 0; i < path.size(); i++) { BetterBlockPos pos = path.get(i); if (positionFirstSeen.containsKey(pos)) { int j = positionFirstSeen.get(pos); while (i > j) { path.remove(i); i--; } } else { positionFirstSeen.put(pos, i); } } return path; } public boolean isFinished() { return this.finished; } public static UnpackedSegment from(final PathSegment segment) { return new UnpackedSegment( Arrays.stream(segment.packed).mapToObj(BetterBlockPos::deserializeFromLong), segment.finished ); } } ================================================ FILE: src/main/java/baritone/selection/Selection.java ================================================ package baritone.selection; import baritone.api.selection.ISelection; import baritone.api.utils.BetterBlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.world.phys.AABB; public class Selection implements ISelection { private final BetterBlockPos pos1; private final BetterBlockPos pos2; private final BetterBlockPos min; private final BetterBlockPos max; private final Vec3i size; private final AABB aabb; public Selection(BetterBlockPos pos1, BetterBlockPos pos2) { this.pos1 = pos1; this.pos2 = pos2; this.min = new BetterBlockPos( Math.min(pos1.x, pos2.x), Math.min(pos1.y, pos2.y), Math.min(pos1.z, pos2.z) ); this.max = new BetterBlockPos( Math.max(pos1.x, pos2.x), Math.max(pos1.y, pos2.y), Math.max(pos1.z, pos2.z) ); this.size = new Vec3i( max.x - min.x + 1, max.y - min.y + 1, max.z - min.z + 1 ); this.aabb = new AABB(this.min, this.max.offset(1, 1, 1)); } @Override public BetterBlockPos pos1() { return pos1; } @Override public BetterBlockPos pos2() { return pos2; } @Override public BetterBlockPos min() { return min; } @Override public BetterBlockPos max() { return max; } @Override public Vec3i size() { return size; } @Override public AABB aabb() { return aabb; } @Override public int hashCode() { return pos1.hashCode() ^ pos2.hashCode(); } @Override public String toString() { return String.format("Selection{pos1=%s,pos2=%s}", pos1, pos2); } /** * Since it might not be immediately obvious what this does, let me explain. * <p> * Let's say you specify Direction.UP, this functions returns if pos2 is the highest BlockPos. * If you specify Direction.DOWN, it returns if pos2 is the lowest BlockPos. * * @param facing The direction to check. * @return {@code true} if pos2 is further in that direction than pos1, {@code false} if it isn't, and something * else if they're both at the same position on that axis (it really doesn't matter) */ private boolean isPos2(Direction facing) { boolean negative = facing.getAxisDirection().getStep() < 0; switch (facing.getAxis()) { case X: return (pos2.x > pos1.x) ^ negative; case Y: return (pos2.y > pos1.y) ^ negative; case Z: return (pos2.z > pos1.z) ^ negative; default: throw new IllegalStateException("Bad Direction.Axis"); } } @Override public ISelection expand(Direction direction, int blocks) { if (isPos2(direction)) { return new Selection(pos1, pos2.relative(direction, blocks)); } else { return new Selection(pos1.relative(direction, blocks), pos2); } } @Override public ISelection contract(Direction direction, int blocks) { if (isPos2(direction)) { return new Selection(pos1.relative(direction, blocks), pos2); } else { return new Selection(pos1, pos2.relative(direction, blocks)); } } @Override public ISelection shift(Direction direction, int blocks) { return new Selection(pos1.relative(direction, blocks), pos2.relative(direction, blocks)); } } ================================================ FILE: src/main/java/baritone/selection/SelectionManager.java ================================================ package baritone.selection; import baritone.Baritone; import baritone.api.selection.ISelection; import baritone.api.selection.ISelectionManager; import baritone.api.utils.BetterBlockPos; import java.util.LinkedList; import java.util.ListIterator; import net.minecraft.core.Direction; public class SelectionManager implements ISelectionManager { private final LinkedList<ISelection> selections = new LinkedList<>(); private ISelection[] selectionsArr = new ISelection[0]; public SelectionManager(Baritone baritone) { new SelectionRenderer(baritone, this); } private void resetSelectionsArr() { selectionsArr = selections.toArray(new ISelection[0]); } @Override public synchronized ISelection addSelection(ISelection selection) { selections.add(selection); resetSelectionsArr(); return selection; } @Override public ISelection addSelection(BetterBlockPos pos1, BetterBlockPos pos2) { return addSelection(new Selection(pos1, pos2)); } @Override public synchronized ISelection removeSelection(ISelection selection) { selections.remove(selection); resetSelectionsArr(); return selection; } @Override public synchronized ISelection[] removeAllSelections() { ISelection[] selectionsArr = getSelections(); selections.clear(); resetSelectionsArr(); return selectionsArr; } @Override public ISelection[] getSelections() { return selectionsArr; } @Override public synchronized ISelection getOnlySelection() { if (selections.size() == 1) { return selections.peekFirst(); } return null; } @Override public ISelection getLastSelection() { return selections.peekLast(); } @Override public synchronized ISelection expand(ISelection selection, Direction direction, int blocks) { for (ListIterator<ISelection> it = selections.listIterator(); it.hasNext(); ) { ISelection current = it.next(); if (current == selection) { it.remove(); it.add(current.expand(direction, blocks)); resetSelectionsArr(); return it.previous(); } } return null; } @Override public synchronized ISelection contract(ISelection selection, Direction direction, int blocks) { for (ListIterator<ISelection> it = selections.listIterator(); it.hasNext(); ) { ISelection current = it.next(); if (current == selection) { it.remove(); it.add(current.contract(direction, blocks)); resetSelectionsArr(); return it.previous(); } } return null; } @Override public synchronized ISelection shift(ISelection selection, Direction direction, int blocks) { for (ListIterator<ISelection> it = selections.listIterator(); it.hasNext(); ) { ISelection current = it.next(); if (current == selection) { it.remove(); it.add(current.shift(direction, blocks)); resetSelectionsArr(); return it.previous(); } } return null; } } ================================================ FILE: src/main/java/baritone/selection/SelectionRenderer.java ================================================ package baritone.selection; import baritone.Baritone; import baritone.api.event.events.RenderEvent; import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.selection.ISelection; import baritone.utils.IRenderer; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.world.phys.AABB; public class SelectionRenderer implements IRenderer, AbstractGameEventListener { public static final double SELECTION_BOX_EXPANSION = .005D; private final SelectionManager manager; SelectionRenderer(Baritone baritone, SelectionManager manager) { this.manager = manager; baritone.getGameEventHandler().registerEventListener(this); } public static void renderSelections(PoseStack stack, ISelection[] selections) { float opacity = settings.selectionOpacity.value; boolean ignoreDepth = settings.renderSelectionIgnoreDepth.value; float lineWidth = settings.selectionLineWidth.value; if (!settings.renderSelection.value || selections.length == 0) { return; } IRenderer.startLines(settings.colorSelection.value, opacity, lineWidth, ignoreDepth); for (ISelection selection : selections) { IRenderer.emitAABB(stack, selection.aabb(), SELECTION_BOX_EXPANSION); } if (settings.renderSelectionCorners.value) { IRenderer.glColor(settings.colorSelectionPos1.value, opacity); for (ISelection selection : selections) { IRenderer.emitAABB(stack, new AABB(selection.pos1(), selection.pos1().offset(1, 1, 1))); } IRenderer.glColor(settings.colorSelectionPos2.value, opacity); for (ISelection selection : selections) { IRenderer.emitAABB(stack, new AABB(selection.pos2(), selection.pos2().offset(1, 1, 1))); } } IRenderer.endLines(ignoreDepth); } @Override public void onRenderPass(RenderEvent event) { renderSelections(event.getModelViewStack(), manager.getSelections()); } } ================================================ FILE: src/main/java/baritone/utils/BaritoneMath.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; /** * @author Brady */ public final class BaritoneMath { private static final double FLOOR_DOUBLE_D = 1_073_741_824.0; private static final int FLOOR_DOUBLE_I = 1_073_741_824; private BaritoneMath() {} public static int fastFloor(final double v) { return (int) (v + FLOOR_DOUBLE_D) - FLOOR_DOUBLE_I; } public static int fastCeil(final double v) { return FLOOR_DOUBLE_I - (int) (FLOOR_DOUBLE_D - v); } } ================================================ FILE: src/main/java/baritone/utils/BaritoneProcessHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.Baritone; import baritone.api.process.IBaritoneProcess; import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; public abstract class BaritoneProcessHelper implements IBaritoneProcess, Helper { protected final Baritone baritone; protected final IPlayerContext ctx; public BaritoneProcessHelper(Baritone baritone) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); } @Override public boolean isTemporary() { return false; } } ================================================ FILE: src/main/java/baritone/utils/BlockBreakHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.api.BaritoneAPI; import baritone.api.utils.IPlayerContext; import baritone.utils.accessor.IPlayerControllerMP; import net.minecraft.world.InteractionHand; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; /** * @author Brady * @since 8/25/2018 */ public final class BlockBreakHelper { // base ticks between block breaks caused by tick logic private static final int BASE_BREAK_DELAY = 1; private final IPlayerContext ctx; private boolean wasHitting; private int breakDelayTimer = 0; BlockBreakHelper(IPlayerContext ctx) { this.ctx = ctx; } public void stopBreakingBlock() { // The player controller will never be null, but the player can be if (ctx.player() != null && wasHitting) { ctx.playerController().setHittingBlock(false); ctx.playerController().resetBlockRemoving(); wasHitting = false; } } public void tick(boolean isLeftClick) { if (breakDelayTimer > 0) { breakDelayTimer--; return; } HitResult trace = ctx.objectMouseOver(); boolean isBlockTrace = trace != null && trace.getType() == HitResult.Type.BLOCK; if (isLeftClick && isBlockTrace) { ctx.playerController().setHittingBlock(wasHitting); if (ctx.playerController().hasBrokenBlock()) { ctx.playerController().syncHeldItem(); ctx.playerController().clickBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection()); ctx.player().swing(InteractionHand.MAIN_HAND); } else { if (ctx.playerController().onPlayerDamageBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection())) { ctx.player().swing(InteractionHand.MAIN_HAND); } if (ctx.playerController().hasBrokenBlock()) { // block broken this tick // break delay timer only applies for multi-tick block breaks like vanilla breakDelayTimer = BaritoneAPI.getSettings().blockBreakSpeed.value - BASE_BREAK_DELAY; // must reset controller's destroy delay to prevent the client from delaying itself unnecessarily ((IPlayerControllerMP) ctx.minecraft().gameMode).setDestroyDelay(0); } } // if true, we're breaking a block. if false, we broke the block this tick wasHitting = !ctx.playerController().hasBrokenBlock(); // this value will be reset by the MC client handling mouse keys // since we're not spoofing the click keybind to the client, the client will stop the break if isDestroyingBlock is true // we store and restore this value on the next tick to determine if we're breaking a block ctx.playerController().setHittingBlock(false); } else { wasHitting = false; } } } ================================================ FILE: src/main/java/baritone/utils/BlockPlaceHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.Baritone; import baritone.api.utils.IPlayerContext; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; public class BlockPlaceHelper { // base ticks between places caused by tick logic private static final int BASE_PLACE_DELAY = 1; private final IPlayerContext ctx; private int rightClickTimer; BlockPlaceHelper(IPlayerContext playerContext) { this.ctx = playerContext; } public void tick(boolean rightClickRequested) { if (rightClickTimer > 0) { rightClickTimer--; return; } HitResult mouseOver = ctx.objectMouseOver(); if (!rightClickRequested || ctx.player().isHandsBusy() || mouseOver == null || mouseOver.getType() != HitResult.Type.BLOCK) { return; } rightClickTimer = Baritone.settings().rightClickSpeed.value - BASE_PLACE_DELAY; for (InteractionHand hand : InteractionHand.values()) { if (ctx.playerController().processRightClickBlock(ctx.player(), ctx.world(), hand, (BlockHitResult) mouseOver) == InteractionResult.SUCCESS) { ctx.player().swing(hand); return; } if (!ctx.player().getItemInHand(hand).isEmpty() && ctx.playerController().processRightClick(ctx.player(), ctx.world(), hand) == InteractionResult.SUCCESS) { return; } } } } ================================================ FILE: src/main/java/baritone/utils/BlockStateInterface.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.Baritone; import baritone.api.utils.IPlayerContext; import baritone.cache.CachedRegion; import baritone.cache.WorldData; import baritone.utils.accessor.IClientChunkProvider; import baritone.utils.pathing.BetterWorldBorder; import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.core.BlockPos; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; /** * Wraps get for chuck caching capability * * @author leijurv */ public class BlockStateInterface { private final ClientChunkCache provider; private final WorldData worldData; protected final Level world; public final BlockPos.MutableBlockPos isPassableBlockPos; public final BlockGetter access; public final BetterWorldBorder worldBorder; private LevelChunk prev = null; private CachedRegion prevCached = null; private final boolean useTheRealWorld; private static final BlockState AIR = Blocks.AIR.defaultBlockState(); public BlockStateInterface(IPlayerContext ctx) { this(ctx, false); } public BlockStateInterface(IPlayerContext ctx, boolean copyLoadedChunks) { this.world = ctx.world(); this.worldBorder = new BetterWorldBorder(world.getWorldBorder()); this.worldData = (WorldData) ctx.worldData(); if (copyLoadedChunks) { this.provider = ((IClientChunkProvider) world.getChunkSource()).createThreadSafeCopy(); } else { this.provider = (ClientChunkCache) world.getChunkSource(); } this.useTheRealWorld = !Baritone.settings().pathThroughCachedOnly.value; if (!ctx.minecraft().isSameThread()) { throw new IllegalStateException("BlockStateInterface must be constructed on the main thread"); } this.isPassableBlockPos = new BlockPos.MutableBlockPos(); this.access = new BlockStateInterfaceAccessWrapper(this); } public boolean worldContainsLoadedChunk(int blockX, int blockZ) { return provider.hasChunk(blockX >> 4, blockZ >> 4); } public static Block getBlock(IPlayerContext ctx, BlockPos pos) { // won't be called from the pathing thread because the pathing thread doesn't make a single blockpos pog return get(ctx, pos).getBlock(); } public static BlockState get(IPlayerContext ctx, BlockPos pos) { return new BlockStateInterface(ctx).get0(pos.getX(), pos.getY(), pos.getZ()); // immense iq // can't just do world().get because that doesn't work for out of bounds // and toBreak and stuff fails when the movement is instantiated out of load range but it's not able to BlockStateInterface.get what it's going to walk on } public BlockState get0(BlockPos pos) { return get0(pos.getX(), pos.getY(), pos.getZ()); } public BlockState get0(int x, int y, int z) { // Mickey resigned y -= world.dimensionType().minY(); // Invalid vertical position if (y < 0 || y >= world.dimensionType().height()) { return AIR; } if (useTheRealWorld) { LevelChunk cached = prev; // there's great cache locality in block state lookups // generally it's within each movement // if it's the same chunk as last time // we can just skip the mc.world.getChunk lookup // which is a Long2ObjectOpenHashMap.get // see issue #113 if (cached != null && cached.getPos().x == x >> 4 && cached.getPos().z == z >> 4) { return getFromChunk(cached, x, y, z); } LevelChunk chunk = provider.getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false); if (chunk != null && !chunk.isEmpty()) { prev = chunk; return getFromChunk(chunk, x, y, z); } } // same idea here, skip the Long2ObjectOpenHashMap.get if at all possible // except here, it's 512x512 tiles instead of 16x16, so even better repetition CachedRegion cached = prevCached; if (cached == null || cached.getX() != x >> 9 || cached.getZ() != z >> 9) { if (worldData == null) { return AIR; } CachedRegion region = worldData.cache.getRegion(x >> 9, z >> 9); if (region == null) { return AIR; } prevCached = region; cached = region; } BlockState type = cached.getBlock(x & 511, y + world.dimensionType().minY(), z & 511); if (type == null) { return AIR; } return type; } public boolean isLoaded(int x, int z) { LevelChunk prevChunk = prev; if (prevChunk != null && prevChunk.getPos().x == x >> 4 && prevChunk.getPos().z == z >> 4) { return true; } prevChunk = provider.getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false); if (prevChunk != null && !prevChunk.isEmpty()) { prev = prevChunk; return true; } CachedRegion prevRegion = prevCached; if (prevRegion != null && prevRegion.getX() == x >> 9 && prevRegion.getZ() == z >> 9) { return prevRegion.isCached(x & 511, z & 511); } if (worldData == null) { return false; } prevRegion = worldData.cache.getRegion(x >> 9, z >> 9); if (prevRegion == null) { return false; } prevCached = prevRegion; return prevRegion.isCached(x & 511, z & 511); } // get the block at x,y,z from this chunk WITHOUT creating a single blockpos object public static BlockState getFromChunk(LevelChunk chunk, int x, int y, int z) { LevelChunkSection section = chunk.getSections()[y >> 4]; if (section.hasOnlyAir()) { return AIR; } return section.getBlockState(x & 15, y & 15, z & 15); } } ================================================ FILE: src/main/java/baritone/utils/BlockStateInterfaceAccessWrapper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; /** * @author Brady * @since 11/5/2019 */ @SuppressWarnings("NullableProblems") public final class BlockStateInterfaceAccessWrapper implements BlockGetter { private final BlockStateInterface bsi; BlockStateInterfaceAccessWrapper(BlockStateInterface bsi) { this.bsi = bsi; } @Nullable @Override public BlockEntity getBlockEntity(BlockPos pos) { return null; } @Override public BlockState getBlockState(BlockPos pos) { // BlockStateInterface#get0(BlockPos) btfo! return this.bsi.get0(pos.getX(), pos.getY(), pos.getZ()); } @Override public FluidState getFluidState(BlockPos blockPos) { return getBlockState(blockPos).getFluidState(); } @Override public int getHeight() { return bsi.world.getHeight(); } @Override public int getMinBuildHeight() { return bsi.world.getMinBuildHeight(); } } ================================================ FILE: src/main/java/baritone/utils/GuiClick.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.ClickEvent; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ClipContext; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Vector4f; import java.awt.*; import java.util.Collections; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class GuiClick extends Screen implements Helper { private Matrix4f projectionViewMatrix; private BlockPos clickStart; private BlockPos currentMouseOver; public GuiClick() { super(Component.literal("CLICK")); } @Override public boolean isPauseScreen() { return false; } @Override public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) { double mx = mc.mouseHandler.xpos(); double my = mc.mouseHandler.ypos(); my = mc.getWindow().getScreenHeight() - my; my *= mc.getWindow().getHeight() / (double) mc.getWindow().getScreenHeight(); mx *= mc.getWindow().getWidth() / (double) mc.getWindow().getScreenWidth(); Vec3 near = toWorld(mx, my, 0); Vec3 far = toWorld(mx, my, 1); // "Use 0.945 that's what stack overflow says" - leijurv if (near != null && far != null) { Vec3 viewerPos = new Vec3(PathRenderer.posX(), PathRenderer.posY(), PathRenderer.posZ()); LocalPlayer player = BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().player(); HitResult result = player.level.clip(new ClipContext(near.add(viewerPos), far.add(viewerPos), ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player)); if (result != null && result.getType() == HitResult.Type.BLOCK) { currentMouseOver = ((BlockHitResult) result).getBlockPos(); } } } @Override public boolean mouseReleased(double mouseX, double mouseY, int mouseButton) { if (currentMouseOver != null) { //Catch this, or else a click into void will result in a crash if (mouseButton == 0) { if (clickStart != null && !clickStart.equals(currentMouseOver)) { BaritoneAPI.getProvider().getPrimaryBaritone().getSelectionManager().removeAllSelections(); BaritoneAPI.getProvider().getPrimaryBaritone().getSelectionManager().addSelection(BetterBlockPos.from(clickStart), BetterBlockPos.from(currentMouseOver)); MutableComponent component = Component.literal("Selection made! For usage: " + Baritone.settings().prefix.value + "help sel"); component.setStyle(component.getStyle() .withColor(ChatFormatting.WHITE) .withClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + "help sel" ))); Helper.HELPER.logDirect(component); clickStart = null; } else { BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalBlock(currentMouseOver)); } } else if (mouseButton == 1) { BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalBlock(currentMouseOver.above())); } } clickStart = null; return super.mouseReleased(mouseX, mouseY, mouseButton); } @Override public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { clickStart = currentMouseOver; return super.mouseClicked(mouseX, mouseY, mouseButton); } public void onRender(PoseStack modelViewStack, Matrix4f projectionMatrix) { this.projectionViewMatrix = new Matrix4f(projectionMatrix); this.projectionViewMatrix.mul(modelViewStack.last().pose()); this.projectionViewMatrix.invert(); if (currentMouseOver != null) { Entity e = mc.getCameraEntity(); // drawSingleSelectionBox WHEN? PathRenderer.drawManySelectionBoxes(modelViewStack, e, Collections.singletonList(currentMouseOver), Color.CYAN); if (clickStart != null && !clickStart.equals(currentMouseOver)) { IRenderer.startLines(Color.RED, Baritone.settings().pathRenderLineWidthPixels.value, true); BetterBlockPos a = new BetterBlockPos(currentMouseOver); BetterBlockPos b = new BetterBlockPos(clickStart); IRenderer.emitAABB(modelViewStack, new AABB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1)); IRenderer.endLines(true); } } } private Vec3 toWorld(double x, double y, double z) { if (this.projectionViewMatrix == null) { return null; } x /= mc.getWindow().getWidth(); y /= mc.getWindow().getHeight(); x = x * 2 - 1; y = y * 2 - 1; Vector4f pos = new Vector4f((float) x, (float) y, (float) z, 1.0F); projectionViewMatrix.transform(pos); if (pos.w() == 0) { return null; } pos.mul(1/pos.w()); return new Vec3(pos.x(), pos.y(), pos.z()); } } ================================================ FILE: src/main/java/baritone/utils/IRenderer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.api.BaritoneAPI; import baritone.api.Settings; import baritone.utils.accessor.IEntityRenderManager; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.joml.Matrix3f; import org.joml.Matrix4f; import java.awt.*; public interface IRenderer { Tesselator tessellator = Tesselator.getInstance(); BufferBuilder buffer = tessellator.getBuilder(); IEntityRenderManager renderManager = (IEntityRenderManager) Minecraft.getInstance().getEntityRenderDispatcher(); TextureManager textureManager = Minecraft.getInstance().getTextureManager(); Settings settings = BaritoneAPI.getSettings(); float[] color = new float[]{1.0F, 1.0F, 1.0F, 255.0F}; static void glColor(Color color, float alpha) { float[] colorComponents = color.getColorComponents(null); IRenderer.color[0] = colorComponents[0]; IRenderer.color[1] = colorComponents[1]; IRenderer.color[2] = colorComponents[2]; IRenderer.color[3] = alpha; } static void startLines(Color color, float alpha, float lineWidth, boolean ignoreDepth) { RenderSystem.enableBlend(); RenderSystem.setShader(GameRenderer::getPositionColorShader); RenderSystem.blendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO ); glColor(color, alpha); RenderSystem.lineWidth(lineWidth); RenderSystem.depthMask(false); RenderSystem.disableCull(); if (ignoreDepth) { RenderSystem.disableDepthTest(); } RenderSystem.setShader(GameRenderer::getRendertypeLinesShader); buffer.begin(VertexFormat.Mode.LINES, DefaultVertexFormat.POSITION_COLOR_NORMAL); } static void startLines(Color color, float lineWidth, boolean ignoreDepth) { startLines(color, .4f, lineWidth, ignoreDepth); } static void endLines(boolean ignoredDepth) { tessellator.end(); if (ignoredDepth) { RenderSystem.enableDepthTest(); } RenderSystem.enableCull(); RenderSystem.depthMask(true); RenderSystem.disableBlend(); } static void emitLine(PoseStack stack, double x1, double y1, double z1, double x2, double y2, double z2) { final double dx = x2 - x1; final double dy = y2 - y1; final double dz = z2 - z1; final double invMag = 1.0 / Math.sqrt(dx * dx + dy * dy + dz * dz); final float nx = (float) (dx * invMag); final float ny = (float) (dy * invMag); final float nz = (float) (dz * invMag); emitLine(stack, x1, y1, z1, x2, y2, z2, nx, ny, nz); } static void emitLine(PoseStack stack, double x1, double y1, double z1, double x2, double y2, double z2, double nx, double ny, double nz) { emitLine(stack, (float) x1, (float) y1, (float) z1, (float) x2, (float) y2, (float) z2, (float) nx, (float) ny, (float) nz ); } static void emitLine(PoseStack stack, float x1, float y1, float z1, float x2, float y2, float z2, float nx, float ny, float nz) { final Matrix4f matrix4f = stack.last().pose(); final Matrix3f normal = stack.last().normal(); buffer.vertex(matrix4f, x1, y1, z1).color(color[0], color[1], color[2], color[3]).normal(normal, nx, ny, nz).endVertex(); buffer.vertex(matrix4f, x2, y2, z2).color(color[0], color[1], color[2], color[3]).normal(normal, nx, ny, nz).endVertex(); } static void emitAABB(PoseStack stack, AABB aabb) { AABB toDraw = aabb.move(-renderManager.renderPosX(), -renderManager.renderPosY(), -renderManager.renderPosZ()); // bottom emitLine(stack, toDraw.minX, toDraw.minY, toDraw.minZ, toDraw.maxX, toDraw.minY, toDraw.minZ, 1.0, 0.0, 0.0); emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.minZ, toDraw.maxX, toDraw.minY, toDraw.maxZ, 0.0, 0.0, 1.0); emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.maxZ, toDraw.minX, toDraw.minY, toDraw.maxZ, -1.0, 0.0, 0.0); emitLine(stack, toDraw.minX, toDraw.minY, toDraw.maxZ, toDraw.minX, toDraw.minY, toDraw.minZ, 0.0, 0.0, -1.0); // top emitLine(stack, toDraw.minX, toDraw.maxY, toDraw.minZ, toDraw.maxX, toDraw.maxY, toDraw.minZ, 1.0, 0.0, 0.0); emitLine(stack, toDraw.maxX, toDraw.maxY, toDraw.minZ, toDraw.maxX, toDraw.maxY, toDraw.maxZ, 0.0, 0.0, 1.0); emitLine(stack, toDraw.maxX, toDraw.maxY, toDraw.maxZ, toDraw.minX, toDraw.maxY, toDraw.maxZ, -1.0, 0.0, 0.0); emitLine(stack, toDraw.minX, toDraw.maxY, toDraw.maxZ, toDraw.minX, toDraw.maxY, toDraw.minZ, 0.0, 0.0, -1.0); // corners emitLine(stack, toDraw.minX, toDraw.minY, toDraw.minZ, toDraw.minX, toDraw.maxY, toDraw.minZ, 0.0, 1.0, 0.0); emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.minZ, toDraw.maxX, toDraw.maxY, toDraw.minZ, 0.0, 1.0, 0.0); emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.maxZ, toDraw.maxX, toDraw.maxY, toDraw.maxZ, 0.0, 1.0, 0.0); emitLine(stack, toDraw.minX, toDraw.minY, toDraw.maxZ, toDraw.minX, toDraw.maxY, toDraw.maxZ, 0.0, 1.0, 0.0); } static void emitAABB(PoseStack stack, AABB aabb, double expand) { emitAABB(stack, aabb.inflate(expand, expand, expand)); } static void emitLine(PoseStack stack, Vec3 start, Vec3 end) { double vpX = renderManager.renderPosX(); double vpY = renderManager.renderPosY(); double vpZ = renderManager.renderPosZ(); emitLine(stack, start.x - vpX, start.y - vpY, start.z - vpZ, end.x - vpX, end.y - vpY, end.z - vpZ); } } ================================================ FILE: src/main/java/baritone/utils/InputOverrideHandler.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.event.events.TickEvent; import baritone.api.utils.IInputOverrideHandler; import baritone.api.utils.input.Input; import baritone.behavior.Behavior; import net.minecraft.client.player.KeyboardInput; import java.util.HashMap; import java.util.Map; /** * An interface with the game's control system allowing the ability to * force down certain controls, having the same effect as if we were actually * physically forcing down the assigned key. * * @author Brady * @since 7/31/2018 */ public final class InputOverrideHandler extends Behavior implements IInputOverrideHandler { /** * Maps inputs to whether or not we are forcing their state down. */ private final Map<Input, Boolean> inputForceStateMap = new HashMap<>(); private final BlockBreakHelper blockBreakHelper; private final BlockPlaceHelper blockPlaceHelper; public InputOverrideHandler(Baritone baritone) { super(baritone); this.blockBreakHelper = new BlockBreakHelper(baritone.getPlayerContext()); this.blockPlaceHelper = new BlockPlaceHelper(baritone.getPlayerContext()); } /** * Returns whether or not we are forcing down the specified {@link Input}. * * @param input The input * @return Whether or not it is being forced down */ @Override public final boolean isInputForcedDown(Input input) { return input == null ? false : this.inputForceStateMap.getOrDefault(input, false); } /** * Sets whether or not the specified {@link Input} is being forced down. * * @param input The {@link Input} * @param forced Whether or not the state is being forced */ @Override public final void setInputForceState(Input input, boolean forced) { this.inputForceStateMap.put(input, forced); } /** * Clears the override state for all keys */ @Override public final void clearAllKeys() { this.inputForceStateMap.clear(); } @Override public final void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.OUT) { return; } if (isInputForcedDown(Input.CLICK_LEFT)) { setInputForceState(Input.CLICK_RIGHT, false); } blockBreakHelper.tick(isInputForcedDown(Input.CLICK_LEFT)); blockPlaceHelper.tick(isInputForcedDown(Input.CLICK_RIGHT)); if (inControl()) { if (ctx.player().input.getClass() != PlayerMovementInput.class) { ctx.player().input = new PlayerMovementInput(this); } } else { if (ctx.player().input.getClass() == PlayerMovementInput.class) { // allow other movement inputs that aren't this one, e.g. for a freecam ctx.player().input = new KeyboardInput(ctx.minecraft().options); } } // only set it if it was previously incorrect // gotta do it this way, or else it constantly thinks you're beginning a double tap W sprint lol } private boolean inControl() { for (Input input : new Input[]{Input.MOVE_FORWARD, Input.MOVE_BACK, Input.MOVE_LEFT, Input.MOVE_RIGHT, Input.SNEAK, Input.JUMP}) { if (isInputForcedDown(input)) { return true; } } // if we are not primary (a bot) we should set the movementinput even when idle (not pathing) return baritone.getPathingBehavior().isPathing() || baritone != BaritoneAPI.getProvider().getPrimaryBaritone(); } public BlockBreakHelper getBlockBreakHelper() { return blockBreakHelper; } } ================================================ FILE: src/main/java/baritone/utils/PathRenderer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.api.BaritoneAPI; import baritone.api.event.events.RenderEvent; import baritone.api.pathing.goals.*; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.IPlayerContext; import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.behavior.PathingBehavior; import baritone.pathing.path.PathExecutor; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.renderer.blockentity.BeaconRenderer; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import java.awt.*; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; /** * @author Brady * @since 8/9/2018 */ public final class PathRenderer implements IRenderer { private static final ResourceLocation TEXTURE_BEACON_BEAM = new ResourceLocation("textures/entity/beacon_beam.png"); private PathRenderer() {} public static double posX() { return renderManager.renderPosX(); } public static double posY() { return renderManager.renderPosY(); } public static double posZ() { return renderManager.renderPosZ(); } public static void render(RenderEvent event, PathingBehavior behavior) { final IPlayerContext ctx = behavior.ctx; if (ctx.world() == null) { return; } if (ctx.minecraft().screen instanceof GuiClick) { ((GuiClick) ctx.minecraft().screen).onRender(event.getModelViewStack(), event.getProjectionMatrix()); } final float partialTicks = event.getPartialTicks(); final Goal goal = behavior.getGoal(); final DimensionType thisPlayerDimension = ctx.world().dimensionType(); final DimensionType currentRenderViewDimension = BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world().dimensionType(); if (thisPlayerDimension != currentRenderViewDimension) { // this is a path for a bot in a different dimension, don't render it return; } if (goal != null && settings.renderGoal.value) { drawGoal(event.getModelViewStack(), ctx, goal, partialTicks, settings.colorGoalBox.value); } if (!settings.renderPath.value) { return; } PathExecutor current = behavior.getCurrent(); // this should prevent most race conditions? PathExecutor next = behavior.getNext(); // like, now it's not possible for current!=null to be true, then suddenly false because of another thread if (current != null && settings.renderSelectionBoxes.value) { drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), current.toBreak(), settings.colorBlocksToBreak.value); drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), current.toPlace(), settings.colorBlocksToPlace.value); drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), current.toWalkInto(), settings.colorBlocksToWalkInto.value); } //drawManySelectionBoxes(player, Collections.singletonList(behavior.pathStart()), partialTicks, Color.WHITE); // Render the current path, if there is one if (current != null && current.getPath() != null) { int renderBegin = Math.max(current.getPosition() - 3, 0); drawPath(event.getModelViewStack(), current.getPath().positions(), renderBegin, settings.colorCurrentPath.value, settings.fadePath.value, 10, 20); } if (next != null && next.getPath() != null) { drawPath(event.getModelViewStack(), next.getPath().positions(), 0, settings.colorNextPath.value, settings.fadePath.value, 10, 20); } // If there is a path calculation currently running, render the path calculation process behavior.getInProgress().ifPresent(currentlyRunning -> { currentlyRunning.bestPathSoFar().ifPresent(p -> { drawPath(event.getModelViewStack(), p.positions(), 0, settings.colorBestPathSoFar.value, settings.fadePath.value, 10, 20); }); currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> { drawPath(event.getModelViewStack(), mr.positions(), 0, settings.colorMostRecentConsidered.value, settings.fadePath.value, 10, 20); drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), Collections.singletonList(mr.getDest()), settings.colorMostRecentConsidered.value); }); }); } public static void drawPath(PoseStack stack, List<BetterBlockPos> positions, int startIndex, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) { drawPath(stack, positions, startIndex, color, fadeOut, fadeStart0, fadeEnd0, 0.5D); } public static void drawPath(PoseStack stack, List<BetterBlockPos> positions, int startIndex, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0, double offset) { IRenderer.startLines(color, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); int fadeStart = fadeStart0 + startIndex; int fadeEnd = fadeEnd0 + startIndex; for (int i = startIndex, next; i < positions.size() - 1; i = next) { BetterBlockPos start = positions.get(i); BetterBlockPos end = positions.get(next = i + 1); int dirX = end.x - start.x; int dirY = end.y - start.y; int dirZ = end.z - start.z; while (next + 1 < positions.size() && (!fadeOut || next + 1 < fadeStart) && (dirX == positions.get(next + 1).x - end.x && dirY == positions.get(next + 1).y - end.y && dirZ == positions.get(next + 1).z - end.z)) { end = positions.get(++next); } if (fadeOut) { float alpha; if (i <= fadeStart) { alpha = 0.4F; } else { if (i > fadeEnd) { break; } alpha = 0.4F * (1.0F - (float) (i - fadeStart) / (float) (fadeEnd - fadeStart)); } IRenderer.glColor(color, alpha); } emitPathLine(stack, start.x, start.y, start.z, end.x, end.y, end.z, offset); } IRenderer.endLines(settings.renderPathIgnoreDepth.value); } private static void emitPathLine(PoseStack stack, double x1, double y1, double z1, double x2, double y2, double z2, double offset) { final double extraOffset = offset + 0.03D; double vpX = posX(); double vpY = posY(); double vpZ = posZ(); boolean renderPathAsFrickinThingy = !settings.renderPathAsLine.value; IRenderer.emitLine(stack, x1 + offset - vpX, y1 + offset - vpY, z1 + offset - vpZ, x2 + offset - vpX, y2 + offset - vpY, z2 + offset - vpZ ); if (renderPathAsFrickinThingy) { IRenderer.emitLine(stack, x2 + offset - vpX, y2 + offset - vpY, z2 + offset - vpZ, x2 + offset - vpX, y2 + extraOffset - vpY, z2 + offset - vpZ ); IRenderer.emitLine(stack, x2 + offset - vpX, y2 + extraOffset - vpY, z2 + offset - vpZ, x1 + offset - vpX, y1 + extraOffset - vpY, z1 + offset - vpZ ); IRenderer.emitLine(stack, x1 + offset - vpX, y1 + extraOffset - vpY, z1 + offset - vpZ, x1 + offset - vpX, y1 + offset - vpY, z1 + offset - vpZ ); } } public static void drawManySelectionBoxes(PoseStack stack, Entity player, Collection<BlockPos> positions, Color color) { IRenderer.startLines(color, settings.pathRenderLineWidthPixels.value, settings.renderSelectionBoxesIgnoreDepth.value); //BlockPos blockpos = movingObjectPositionIn.getBlockPos(); BlockStateInterface bsi = new BlockStateInterface(BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext()); // TODO this assumes same dimension between primary baritone and render view? is this safe? positions.forEach(pos -> { BlockState state = bsi.get0(pos); VoxelShape shape = state.getShape(player.level, pos); AABB toDraw = shape.isEmpty() ? Shapes.block().bounds() : shape.bounds(); toDraw = toDraw.move(pos); IRenderer.emitAABB(stack, toDraw, .002D); }); IRenderer.endLines(settings.renderSelectionBoxesIgnoreDepth.value); } public static void drawGoal(PoseStack stack, IPlayerContext ctx, Goal goal, float partialTicks, Color color) { drawGoal(stack, ctx, goal, partialTicks, color, true); } private static void drawGoal(PoseStack stack, IPlayerContext ctx, Goal goal, float partialTicks, Color color, boolean setupRender) { double renderPosX = posX(); double renderPosY = posY(); double renderPosZ = posZ(); double minX, maxX; double minZ, maxZ; double minY, maxY; double y, y1, y2; if (!settings.renderGoalAnimated.value) { // y = 1 causes rendering issues when the player is at the same y as the top of a block for some reason y = 0.999F; } else { y = Mth.cos((float) (((float) ((System.nanoTime() / 100000L) % 20000L)) / 20000F * Math.PI * 2)); } if (goal instanceof IGoalRenderPos) { BlockPos goalPos = ((IGoalRenderPos) goal).getGoalPos(); minX = goalPos.getX() + 0.002 - renderPosX; maxX = goalPos.getX() + 1 - 0.002 - renderPosX; minZ = goalPos.getZ() + 0.002 - renderPosZ; maxZ = goalPos.getZ() + 1 - 0.002 - renderPosZ; if (goal instanceof GoalGetToBlock || goal instanceof GoalTwoBlocks) { y /= 2; } y1 = 1 + y + goalPos.getY() - renderPosY; y2 = 1 - y + goalPos.getY() - renderPosY; minY = goalPos.getY() - renderPosY; maxY = minY + 2; if (goal instanceof GoalGetToBlock || goal instanceof GoalTwoBlocks) { y1 -= 0.5; y2 -= 0.5; maxY--; } drawDankLitGoalBox(stack, color, minX, maxX, minZ, maxZ, minY, maxY, y1, y2, setupRender); } else if (goal instanceof GoalXZ) { GoalXZ goalPos = (GoalXZ) goal; minY = ctx.world().getMinBuildHeight(); maxY = ctx.world().getMaxBuildHeight(); if (settings.renderGoalXZBeacon.value) { //TODO: check textureManager.bindForSetup(TEXTURE_BEACON_BEAM); if (settings.renderGoalIgnoreDepth.value) { RenderSystem.disableDepthTest(); } stack.pushPose(); // push stack.translate(goalPos.getX() - renderPosX, -renderPosY, goalPos.getZ() - renderPosZ); // translate //TODO: check BeaconRenderer.renderBeaconBeam( stack, ctx.minecraft().renderBuffers().bufferSource(), TEXTURE_BEACON_BEAM, settings.renderGoalAnimated.value ? partialTicks : 0, 1.0F, settings.renderGoalAnimated.value ? ctx.world().getGameTime() : 0, (int) minY, (int) maxY, color.getColorComponents(null), // Arguments filled by the private method lol 0.2F, 0.25F ); stack.popPose(); // pop if (settings.renderGoalIgnoreDepth.value) { RenderSystem.enableDepthTest(); } return; } minX = goalPos.getX() + 0.002 - renderPosX; maxX = goalPos.getX() + 1 - 0.002 - renderPosX; minZ = goalPos.getZ() + 0.002 - renderPosZ; maxZ = goalPos.getZ() + 1 - 0.002 - renderPosZ; y1 = 0; y2 = 0; minY -= renderPosY; maxY -= renderPosY; drawDankLitGoalBox(stack, color, minX, maxX, minZ, maxZ, minY, maxY, y1, y2, setupRender); } else if (goal instanceof GoalComposite) { // Simple way to determine if goals can be batched, without having some sort of GoalRenderer boolean batch = Arrays.stream(((GoalComposite) goal).goals()).allMatch(IGoalRenderPos.class::isInstance); if (batch) { IRenderer.startLines(color, settings.goalRenderLineWidthPixels.value, settings.renderGoalIgnoreDepth.value); } for (Goal g : ((GoalComposite) goal).goals()) { drawGoal(stack, ctx, g, partialTicks, color, !batch); } if (batch) { IRenderer.endLines(settings.renderGoalIgnoreDepth.value); } } else if (goal instanceof GoalInverted) { drawGoal(stack, ctx, ((GoalInverted) goal).origin, partialTicks, settings.colorInvertedGoalBox.value); } else if (goal instanceof GoalYLevel) { GoalYLevel goalpos = (GoalYLevel) goal; minX = ctx.player().position().x - settings.yLevelBoxSize.value - renderPosX; minZ = ctx.player().position().z - settings.yLevelBoxSize.value - renderPosZ; maxX = ctx.player().position().x + settings.yLevelBoxSize.value - renderPosX; maxZ = ctx.player().position().z + settings.yLevelBoxSize.value - renderPosZ; minY = ((GoalYLevel) goal).level - renderPosY; maxY = minY + 2; y1 = 1 + y + goalpos.level - renderPosY; y2 = 1 - y + goalpos.level - renderPosY; drawDankLitGoalBox(stack, color, minX, maxX, minZ, maxZ, minY, maxY, y1, y2, setupRender); } } private static void drawDankLitGoalBox(PoseStack stack, Color colorIn, double minX, double maxX, double minZ, double maxZ, double minY, double maxY, double y1, double y2, boolean setupRender) { if (setupRender) { IRenderer.startLines(colorIn, settings.goalRenderLineWidthPixels.value, settings.renderGoalIgnoreDepth.value); } renderHorizontalQuad(stack, minX, maxX, minZ, maxZ, y1); renderHorizontalQuad(stack, minX, maxX, minZ, maxZ, y2); for (double y = minY; y < maxY; y += 16) { double max = Math.min(maxY, y + 16); IRenderer.emitLine(stack, minX, y, minZ, minX, max, minZ, 0.0, 1.0, 0.0); IRenderer.emitLine(stack, maxX, y, minZ, maxX, max, minZ, 0.0, 1.0, 0.0); IRenderer.emitLine(stack, maxX, y, maxZ, maxX, max, maxZ, 0.0, 1.0, 0.0); IRenderer.emitLine(stack, minX, y, maxZ, minX, max, maxZ, 0.0, 1.0, 0.0); } if (setupRender) { IRenderer.endLines(settings.renderGoalIgnoreDepth.value); } } private static void renderHorizontalQuad(PoseStack stack, double minX, double maxX, double minZ, double maxZ, double y) { if (y != 0) { IRenderer.emitLine(stack, minX, y, minZ, maxX, y, minZ, 1.0, 0.0, 0.0); IRenderer.emitLine(stack, maxX, y, minZ, maxX, y, maxZ, 0.0, 0.0, 1.0); IRenderer.emitLine(stack, maxX, y, maxZ, minX, y, maxZ, -1.0, 0.0, 0.0); IRenderer.emitLine(stack, minX, y, maxZ, minX, y, minZ, 0.0, 0.0, -1.0); } } } ================================================ FILE: src/main/java/baritone/utils/PathingCommandContext.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.api.pathing.goals.Goal; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.pathing.movement.CalculationContext; public class PathingCommandContext extends PathingCommand { public final CalculationContext desiredCalcContext; public PathingCommandContext(Goal goal, PathingCommandType commandType, CalculationContext context) { super(goal, commandType); this.desiredCalcContext = context; } } ================================================ FILE: src/main/java/baritone/utils/PathingControlManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.Baritone; import baritone.api.event.events.TickEvent; import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.pathing.calc.IPathingControlManager; import baritone.api.pathing.goals.Goal; import baritone.api.process.IBaritoneProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.behavior.PathingBehavior; import baritone.pathing.path.PathExecutor; import net.minecraft.core.BlockPos; import java.util.*; public class PathingControlManager implements IPathingControlManager { private final Baritone baritone; private final HashSet<IBaritoneProcess> processes; // unGh private final List<IBaritoneProcess> active; private IBaritoneProcess inControlLastTick; private IBaritoneProcess inControlThisTick; private PathingCommand command; public PathingControlManager(Baritone baritone) { this.baritone = baritone; this.processes = new HashSet<>(); this.active = new ArrayList<>(); baritone.getGameEventHandler().registerEventListener(new AbstractGameEventListener() { // needs to be after all behavior ticks @Override public void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.IN) { postTick(); } } }); } @Override public void registerProcess(IBaritoneProcess process) { process.onLostControl(); // make sure it's reset processes.add(process); } public void cancelEverything() { // called by PathingBehavior on TickEvent Type OUT inControlLastTick = null; inControlThisTick = null; command = null; active.clear(); for (IBaritoneProcess proc : processes) { proc.onLostControl(); if (proc.isActive() && !proc.isTemporary()) { // it's okay only for a temporary thing (like combat pause) to maintain control even if you say to cancel throw new IllegalStateException(proc.displayName() + " stayed active after being cancelled"); } } } @Override public Optional<IBaritoneProcess> mostRecentInControl() { return Optional.ofNullable(inControlThisTick); } @Override public Optional<PathingCommand> mostRecentCommand() { return Optional.ofNullable(command); } public void preTick() { inControlLastTick = inControlThisTick; inControlThisTick = null; PathingBehavior p = baritone.getPathingBehavior(); command = executeProcesses(); if (command == null) { p.cancelSegmentIfSafe(); p.secretInternalSetGoal(null); return; } if (!Objects.equals(inControlThisTick, inControlLastTick) && command.commandType != PathingCommandType.REQUEST_PAUSE && inControlLastTick != null && !inControlLastTick.isTemporary()) { // if control has changed from a real process to another real process, and the new process wants to do something p.cancelSegmentIfSafe(); // get rid of the in progress stuff from the last process } switch (command.commandType) { case SET_GOAL_AND_PAUSE: p.secretInternalSetGoalAndPath(command); case REQUEST_PAUSE: p.requestPause(); break; case CANCEL_AND_SET_GOAL: p.secretInternalSetGoal(command.goal); p.cancelSegmentIfSafe(); break; case FORCE_REVALIDATE_GOAL_AND_PATH: case REVALIDATE_GOAL_AND_PATH: if (!p.isPathing() && !p.getInProgress().isPresent()) { p.secretInternalSetGoalAndPath(command); } break; case SET_GOAL_AND_PATH: // now this i can do if (command.goal != null) { p.secretInternalSetGoalAndPath(command); } break; default: throw new IllegalStateException("Unexpected command type " + command.commandType); } } private void postTick() { // if we did this in pretick, it would suck // we use the time between ticks as calculation time // therefore, we only cancel and recalculate after the tick for the current path has executed // "it would suck" means it would actually execute a path every other tick if (command == null) { return; } PathingBehavior p = baritone.getPathingBehavior(); switch (command.commandType) { case FORCE_REVALIDATE_GOAL_AND_PATH: if (command.goal == null || forceRevalidate(command.goal) || revalidateGoal(command.goal)) { // pwnage p.softCancelIfSafe(); } p.secretInternalSetGoalAndPath(command); break; case REVALIDATE_GOAL_AND_PATH: if (Baritone.settings().cancelOnGoalInvalidation.value && (command.goal == null || revalidateGoal(command.goal))) { p.softCancelIfSafe(); } p.secretInternalSetGoalAndPath(command); break; default: } } public boolean forceRevalidate(Goal newGoal) { PathExecutor current = baritone.getPathingBehavior().getCurrent(); if (current != null) { if (newGoal.isInGoal(current.getPath().getDest())) { return false; } return !newGoal.equals(current.getPath().getGoal()); } return false; } public boolean revalidateGoal(Goal newGoal) { PathExecutor current = baritone.getPathingBehavior().getCurrent(); if (current != null) { Goal intended = current.getPath().getGoal(); BlockPos end = current.getPath().getDest(); if (intended.isInGoal(end) && !newGoal.isInGoal(end)) { // this path used to end in the goal // but the goal has changed, so there's no reason to continue... return true; } } return false; } public PathingCommand executeProcesses() { for (IBaritoneProcess process : processes) { if (process.isActive()) { if (!active.contains(process)) { // put a newly active process at the very front of the queue active.add(0, process); } } else { active.remove(process); } } // ties are broken by which was added to the beginning of the list first active.sort(Comparator.comparingDouble(IBaritoneProcess::priority).reversed()); Iterator<IBaritoneProcess> iterator = active.iterator(); while (iterator.hasNext()) { IBaritoneProcess proc = iterator.next(); PathingCommand exec = proc.onTick(Objects.equals(proc, inControlLastTick) && baritone.getPathingBehavior().calcFailedLastTick(), baritone.getPathingBehavior().isSafeToCancel()); if (exec == null) { if (proc.isActive()) { throw new IllegalStateException(proc.displayName() + " actively returned null PathingCommand"); } // no need to call onLostControl; they are reporting inactive. } else if (exec.commandType != PathingCommandType.DEFER) { inControlThisTick = proc; if (!proc.isTemporary()) { iterator.forEachRemaining(IBaritoneProcess::onLostControl); } return exec; } } return null; } } ================================================ FILE: src/main/java/baritone/utils/PlayerMovementInput.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.api.utils.input.Input; public class PlayerMovementInput extends net.minecraft.client.player.Input { private final InputOverrideHandler handler; PlayerMovementInput(InputOverrideHandler handler) { this.handler = handler; } @Override public void tick(boolean p_225607_1_, float f) { this.leftImpulse = 0.0F; this.forwardImpulse = 0.0F; this.jumping = handler.isInputForcedDown(Input.JUMP); // oppa gangnam style if (this.up = handler.isInputForcedDown(Input.MOVE_FORWARD)) { this.forwardImpulse++; } if (this.down = handler.isInputForcedDown(Input.MOVE_BACK)) { this.forwardImpulse--; } if (this.left = handler.isInputForcedDown(Input.MOVE_LEFT)) { this.leftImpulse++; } if (this.right = handler.isInputForcedDown(Input.MOVE_RIGHT)) { this.leftImpulse--; } if (this.shiftKeyDown = handler.isInputForcedDown(Input.SNEAK)) { this.leftImpulse *= 0.3D; this.forwardImpulse *= 0.3D; } } } ================================================ FILE: src/main/java/baritone/utils/ToolSet.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils; import baritone.Baritone; import net.minecraft.client.player.LocalPlayer; import net.minecraft.tags.ItemTags; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.SwordItem; import net.minecraft.world.item.TieredItem; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import java.util.HashMap; import java.util.Map; import java.util.function.Function; /** * A cached list of the best tools on the hotbar for any block * * @author Avery, Brady, leijurv */ public class ToolSet { /** * A cache mapping a {@link Block} to how long it will take to break * with this toolset, given the optimum tool is used. */ private final Map<Block, Double> breakStrengthCache; /** * My buddy leijurv owned me so we have this to not create a new lambda instance. */ private final Function<Block, Double> backendCalculation; private final LocalPlayer player; public ToolSet(LocalPlayer player) { breakStrengthCache = new HashMap<>(); this.player = player; if (Baritone.settings().considerPotionEffects.value) { double amplifier = potionAmplifier(); Function<Double, Double> amplify = x -> amplifier * x; backendCalculation = amplify.compose(this::getBestDestructionTime); } else { backendCalculation = this::getBestDestructionTime; } } /** * Using the best tool on the hotbar, how fast we can mine this block * * @param state the blockstate to be mined * @return the speed of how fast we'll mine it. 1/(time in ticks) */ public double getStrVsBlock(BlockState state) { return breakStrengthCache.computeIfAbsent(state.getBlock(), backendCalculation); } /** * Evaluate the material cost of a possible tool. The priority matches the * harvest level order; there is a chance for multiple at the same with modded tools * but in that case we don't really care. * * @param itemStack a possibly empty ItemStack * @return values from 0 up */ private int getMaterialCost(ItemStack itemStack) { if (itemStack.getItem() instanceof TieredItem) { TieredItem tool = (TieredItem) itemStack.getItem(); return tool.getTier().getLevel(); } else { return -1; } } public boolean hasSilkTouch(ItemStack stack) { return EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, stack) > 0; } /** * Calculate which tool on the hotbar is best for mining, depending on an override setting, * related to auto tool movement cost, it will either return current selected slot, or the best slot. * * @param b the blockstate to be mined * @return An int containing the index in the tools array that worked best */ public int getBestSlot(Block b, boolean preferSilkTouch) { return getBestSlot(b, preferSilkTouch, false); } public int getBestSlot(Block b, boolean preferSilkTouch, boolean pathingCalculation) { /* If we actually want know what efficiency our held item has instead of the best one possible, this lets us make pathing depend on the actual tool to be used (if auto tool is disabled) */ if (!Baritone.settings().autoTool.value && pathingCalculation) { return player.getInventory().selected; } int best = 0; double highestSpeed = Double.NEGATIVE_INFINITY; int lowestCost = Integer.MIN_VALUE; boolean bestSilkTouch = false; BlockState blockState = b.defaultBlockState(); for (int i = 0; i < 9; i++) { ItemStack itemStack = player.getInventory().getItem(i); if (!Baritone.settings().useSwordToMine.value && itemStack.is(ItemTags.SWORDS)) { continue; } if (Baritone.settings().itemSaver.value && (itemStack.getDamageValue() + Baritone.settings().itemSaverThreshold.value) >= itemStack.getMaxDamage() && itemStack.getMaxDamage() > 1) { continue; } double speed = calculateSpeedVsBlock(itemStack, blockState); boolean silkTouch = hasSilkTouch(itemStack); if (speed > highestSpeed) { highestSpeed = speed; best = i; lowestCost = getMaterialCost(itemStack); bestSilkTouch = silkTouch; } else if (speed == highestSpeed) { int cost = getMaterialCost(itemStack); if ((cost < lowestCost && (silkTouch || !bestSilkTouch)) || (preferSilkTouch && !bestSilkTouch && silkTouch)) { highestSpeed = speed; best = i; lowestCost = cost; bestSilkTouch = silkTouch; } } } return best; } /** * Calculate how effectively a block can be destroyed * * @param b the blockstate to be mined * @return A double containing the destruction ticks with the best tool */ private double getBestDestructionTime(Block b) { ItemStack stack = player.getInventory().getItem(getBestSlot(b, false, true)); return calculateSpeedVsBlock(stack, b.defaultBlockState()) * avoidanceMultiplier(b); } private double avoidanceMultiplier(Block b) { return Baritone.settings().blocksToAvoidBreaking.value.contains(b) ? Baritone.settings().avoidBreakingMultiplier.value : 1; } /** * Calculates how long would it take to mine the specified block given the best tool * in this toolset is used. A negative value is returned if the specified block is unbreakable. * * @param item the item to mine it with * @param state the blockstate to be mined * @return how long it would take in ticks */ public static double calculateSpeedVsBlock(ItemStack item, BlockState state) { float hardness; try { hardness = state.getDestroySpeed(null, null); } catch (NullPointerException npe) { // can't easily determine the hardness so treat it as unbreakable return -1; } if (hardness < 0) { return -1; } float speed = item.getDestroySpeed(state); if (speed > 1) { int effLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY, item); if (effLevel > 0 && !item.isEmpty()) { speed += effLevel * effLevel + 1; } } speed /= hardness; if (!state.requiresCorrectToolForDrops() || (!item.isEmpty() && item.isCorrectToolForDrops(state))) { return speed / 30; } else { return speed / 100; } } /** * Calculates any modifier to breaking time based on status effects. * * @return a double to scale block breaking speed. */ private double potionAmplifier() { double speed = 1; if (player.hasEffect(MobEffects.DIG_SPEED)) { speed *= 1 + (player.getEffect(MobEffects.DIG_SPEED).getAmplifier() + 1) * 0.2; } if (player.hasEffect(MobEffects.DIG_SLOWDOWN)) { switch (player.getEffect(MobEffects.DIG_SLOWDOWN).getAmplifier()) { case 0: speed *= 0.3; break; case 1: speed *= 0.09; break; case 2: speed *= 0.0027; // you might think that 0.09*0.3 = 0.027 so that should be next, that would make too much sense. it's 0.0027. break; default: speed *= 0.00081; break; } } return speed; } } ================================================ FILE: src/main/java/baritone/utils/accessor/IChunkArray.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; import java.util.concurrent.atomic.AtomicReferenceArray; import net.minecraft.world.level.chunk.LevelChunk; public interface IChunkArray { void copyFrom(IChunkArray other); AtomicReferenceArray<LevelChunk> getChunks(); int centerX(); int centerZ(); int viewDistance(); } ================================================ FILE: src/main/java/baritone/utils/accessor/IChunkProviderClient.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import net.minecraft.world.level.chunk.LevelChunk; public interface IChunkProviderClient { Long2ObjectMap<LevelChunk> loadedChunks(); } ================================================ FILE: src/main/java/baritone/utils/accessor/IClientChunkProvider.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; import net.minecraft.client.multiplayer.ClientChunkCache; public interface IClientChunkProvider { ClientChunkCache createThreadSafeCopy(); IChunkArray extractReferenceArray(); } ================================================ FILE: src/main/java/baritone/utils/accessor/IEntityRenderManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; public interface IEntityRenderManager { double renderPosX(); double renderPosY(); double renderPosZ(); } ================================================ FILE: src/main/java/baritone/utils/accessor/IFireworkRocketEntity.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; import net.minecraft.world.entity.LivingEntity; public interface IFireworkRocketEntity { LivingEntity getBoostedEntity(); } ================================================ FILE: src/main/java/baritone/utils/accessor/IGuiScreen.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; import java.net.URI; public interface IGuiScreen { void openLinkInvoker(URI url); } ================================================ FILE: src/main/java/baritone/utils/accessor/IPalettedContainer.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; import net.minecraft.util.BitStorage; import net.minecraft.world.level.chunk.Palette; public interface IPalettedContainer<T> { Palette<T> getPalette(); BitStorage getStorage(); public interface IData<T> { Palette<T> getPalette(); BitStorage getStorage(); } } ================================================ FILE: src/main/java/baritone/utils/accessor/IPlayerControllerMP.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.accessor; import net.minecraft.core.BlockPos; public interface IPlayerControllerMP { void setIsHittingBlock(boolean isHittingBlock); boolean isHittingBlock(); BlockPos getCurrentBlock(); void callSyncCurrentPlayItem(); void setDestroyDelay(int destroyDelay); } ================================================ FILE: src/main/java/baritone/utils/pathing/Avoidance.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; import baritone.Baritone; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.IPlayerContext; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; import java.util.ArrayList; import java.util.Collections; import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.monster.EnderMan; import net.minecraft.world.entity.monster.Spider; import net.minecraft.world.entity.monster.ZombifiedPiglin; public class Avoidance { private final int centerX; private final int centerY; private final int centerZ; private final double coefficient; private final int radius; private final int radiusSq; public Avoidance(BlockPos center, double coefficient, int radius) { this(center.getX(), center.getY(), center.getZ(), coefficient, radius); } public Avoidance(int centerX, int centerY, int centerZ, double coefficient, int radius) { this.centerX = centerX; this.centerY = centerY; this.centerZ = centerZ; this.coefficient = coefficient; this.radius = radius; this.radiusSq = radius * radius; } public double coefficient(int x, int y, int z) { int xDiff = x - centerX; int yDiff = y - centerY; int zDiff = z - centerZ; return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff <= radiusSq ? coefficient : 1.0D; } public static List<Avoidance> create(IPlayerContext ctx) { if (!Baritone.settings().avoidance.value) { return Collections.emptyList(); } List<Avoidance> res = new ArrayList<>(); double mobSpawnerCoeff = Baritone.settings().mobSpawnerAvoidanceCoefficient.value; double mobCoeff = Baritone.settings().mobAvoidanceCoefficient.value; if (mobSpawnerCoeff != 1.0D) { ctx.worldData().getCachedWorld().getLocationsOf("mob_spawner", 1, ctx.playerFeet().x, ctx.playerFeet().z, 2) .forEach(mobspawner -> res.add(new Avoidance(mobspawner, mobSpawnerCoeff, Baritone.settings().mobSpawnerAvoidanceRadius.value))); } if (mobCoeff != 1.0D) { ctx.entitiesStream() .filter(entity -> entity instanceof Mob) .filter(entity -> (!(entity instanceof Spider)) || ctx.player().getLightLevelDependentMagicValue() < 0.5) .filter(entity -> !(entity instanceof ZombifiedPiglin) || ((ZombifiedPiglin) entity).getLastHurtByMob() != null) .filter(entity -> !(entity instanceof EnderMan) || ((EnderMan) entity).isCreepy()) .forEach(entity -> res.add(new Avoidance(entity.blockPosition(), mobCoeff, Baritone.settings().mobAvoidanceRadius.value))); } return res; } public void applySpherical(Long2DoubleOpenHashMap map) { for (int x = -radius; x <= radius; x++) { for (int y = -radius; y <= radius; y++) { for (int z = -radius; z <= radius; z++) { if (x * x + y * y + z * z <= radius * radius) { long hash = BetterBlockPos.longHash(centerX + x, centerY + y, centerZ + z); map.put(hash, map.get(hash) * coefficient); } } } } } } ================================================ FILE: src/main/java/baritone/utils/pathing/BetterWorldBorder.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; import net.minecraft.world.level.border.WorldBorder; /** * Essentially, a "rule" for the path finder, prevents proposed movements from attempting to venture * into the world border, and prevents actual movements from placing blocks in the world border. */ public class BetterWorldBorder { private final double minX; private final double maxX; private final double minZ; private final double maxZ; public BetterWorldBorder(WorldBorder border) { this.minX = border.getMinX(); this.maxX = border.getMaxX(); this.minZ = border.getMinZ(); this.maxZ = border.getMaxZ(); } public boolean entirelyContains(int x, int z) { return x + 1 > minX && x < maxX && z + 1 > minZ && z < maxZ; } public boolean canPlaceAt(int x, int z) { // move it in 1 block on all sides // because we can't place a block at the very edge against a block outside the border // it won't let us right click it return x > minX && x + 1 < maxX && z > minZ && z + 1 < maxZ; } } ================================================ FILE: src/main/java/baritone/utils/pathing/Favoring.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; import baritone.api.pathing.calc.IPath; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; import baritone.pathing.movement.CalculationContext; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; public final class Favoring { private final Long2DoubleOpenHashMap favorings; public Favoring(IPlayerContext ctx, IPath previous, CalculationContext context) { this(previous, context); for (Avoidance avoid : Avoidance.create(ctx)) { avoid.applySpherical(favorings); } Helper.HELPER.logDebug("Favoring size: " + favorings.size()); } public Favoring(IPath previous, CalculationContext context) { // create one just from previous path, no mob avoidances favorings = new Long2DoubleOpenHashMap(); favorings.defaultReturnValue(1.0D); double coeff = context.backtrackCostFavoringCoefficient; if (coeff != 1D && previous != null) { previous.positions().forEach(pos -> favorings.put(BetterBlockPos.longHash(pos), coeff)); } } public boolean isEmpty() { return favorings.isEmpty(); } public double calculate(long hash) { return favorings.get(hash); } } ================================================ FILE: src/main/java/baritone/utils/pathing/MutableMoveResult.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; import baritone.api.pathing.movement.ActionCosts; /** * The result of a calculated movement, with destination x, y, z, and the cost of performing the movement * * @author leijurv */ public final class MutableMoveResult { public int x; public int y; public int z; public double cost; public MutableMoveResult() { reset(); } public final void reset() { x = 0; y = 0; z = 0; cost = ActionCosts.COST_INF; } } ================================================ FILE: src/main/java/baritone/utils/pathing/PathBase.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.pathing.path.CutoffPath; import baritone.utils.BlockStateInterface; import net.minecraft.core.BlockPos; public abstract class PathBase implements IPath { @Override public PathBase cutoffAtLoadedChunks(Object bsi0) { // <-- cursed cursed cursed if (!Baritone.settings().cutoffAtLoadBoundary.value) { return this; } BlockStateInterface bsi = (BlockStateInterface) bsi0; for (int i = 0; i < positions().size(); i++) { BlockPos pos = positions().get(i); if (!bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ())) { return new CutoffPath(this, i); } } return this; } @Override public PathBase staticCutoff(Goal destination) { int min = BaritoneAPI.getSettings().pathCutoffMinimumLength.value; if (length() < min) { return this; } if (destination == null || destination.isInGoal(getDest())) { return this; } double factor = BaritoneAPI.getSettings().pathCutoffFactor.value; int newLength = (int) ((length() - min) * factor) + min - 1; return new CutoffPath(this, newLength); } } ================================================ FILE: src/main/java/baritone/utils/pathing/PathingBlockType.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; /** * @author Brady * @since 8/4/2018 */ public enum PathingBlockType { AIR(0b00), WATER(0b01), AVOID(0b10), SOLID(0b11); private final boolean[] bits; PathingBlockType(int bits) { this.bits = new boolean[]{ (bits & 0b10) != 0, (bits & 0b01) != 0 }; } public final boolean[] getBits() { return this.bits; } public static PathingBlockType fromBits(boolean b1, boolean b2) { return b1 ? b2 ? SOLID : AVOID : b2 ? WATER : AIR; } } ================================================ FILE: src/main/java/baritone/utils/player/BaritonePlayerContext.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.player; import baritone.Baritone; import baritone.api.cache.IWorldData; import baritone.api.utils.*; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.phys.HitResult; /** * Implementation of {@link IPlayerContext} that provides information about the primary player. * * @author Brady * @since 11/12/2018 */ public final class BaritonePlayerContext implements IPlayerContext { private final Baritone baritone; private final Minecraft mc; private final IPlayerController playerController; public BaritonePlayerContext(Baritone baritone, Minecraft mc) { this.baritone = baritone; this.mc = mc; this.playerController = new BaritonePlayerController(mc); } @Override public Minecraft minecraft() { return this.mc; } @Override public LocalPlayer player() { return this.mc.player; } @Override public IPlayerController playerController() { return this.playerController; } @Override public Level world() { return this.mc.level; } @Override public IWorldData worldData() { return this.baritone.getWorldProvider().getCurrentWorld(); } @Override public BetterBlockPos viewerPos() { final Entity entity = this.mc.getCameraEntity(); return entity == null ? this.playerFeet() : BetterBlockPos.from(entity.blockPosition()); } @Override public Rotation playerRotations() { return this.baritone.getLookBehavior().getEffectiveRotation().orElseGet(IPlayerContext.super::playerRotations); } @Override public HitResult objectMouseOver() { return RayTraceUtils.rayTraceTowards(player(), playerRotations(), playerController().getBlockReachDistance()); } } ================================================ FILE: src/main/java/baritone/utils/player/BaritonePlayerController.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.player; import baritone.api.utils.IPlayerController; import baritone.utils.accessor.IPlayerControllerMP; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.ClickType; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.phys.BlockHitResult; /** * Implementation of {@link IPlayerController} that chains to the primary player controller's methods * * @author Brady * @since 12/14/2018 */ public final class BaritonePlayerController implements IPlayerController { private final Minecraft mc; public BaritonePlayerController(Minecraft mc) { this.mc = mc; } @Override public void syncHeldItem() { ((IPlayerControllerMP) mc.gameMode).callSyncCurrentPlayItem(); } @Override public boolean hasBrokenBlock() { return !((IPlayerControllerMP) mc.gameMode).isHittingBlock(); } @Override public boolean onPlayerDamageBlock(BlockPos pos, Direction side) { return mc.gameMode.continueDestroyBlock(pos, side); } @Override public void resetBlockRemoving() { mc.gameMode.stopDestroyBlock(); } @Override public void windowClick(int windowId, int slotId, int mouseButton, ClickType type, Player player) { mc.gameMode.handleInventoryMouseClick(windowId, slotId, mouseButton, type, player); } @Override public GameType getGameType() { return mc.gameMode.getPlayerMode(); } @Override public InteractionResult processRightClickBlock(LocalPlayer player, Level world, InteractionHand hand, BlockHitResult result) { // primaryplayercontroller is always in a ClientWorld so this is ok return mc.gameMode.useItemOn(player, hand, result); } @Override public InteractionResult processRightClick(LocalPlayer player, Level world, InteractionHand hand) { return mc.gameMode.useItem(player, hand); } @Override public boolean clickBlock(BlockPos loc, Direction face) { return mc.gameMode.startDestroyBlock(loc, face); } @Override public void setHittingBlock(boolean hittingBlock) { ((IPlayerControllerMP) mc.gameMode).setIsHittingBlock(hittingBlock); } } ================================================ FILE: src/main/java/baritone/utils/schematic/MapArtSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic; import baritone.api.schematic.IStaticSchematic; import baritone.api.schematic.MaskSchematic; import java.util.OptionalInt; import java.util.function.Predicate; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.state.BlockState; public class MapArtSchematic extends MaskSchematic { private final int[][] heightMap; public MapArtSchematic(IStaticSchematic schematic) { super(schematic); this.heightMap = generateHeightMap(schematic); } @Override protected boolean partOfMask(int x, int y, int z, BlockState currentState) { return y >= this.heightMap[x][z]; } private static int[][] generateHeightMap(IStaticSchematic schematic) { int[][] heightMap = new int[schematic.widthX()][schematic.lengthZ()]; int missingColumns = 0; for (int x = 0; x < schematic.widthX(); x++) { for (int z = 0; z < schematic.lengthZ(); z++) { BlockState[] column = schematic.getColumn(x, z); OptionalInt lowestBlockY = lastIndexMatching(column, state -> !(state.getBlock() instanceof AirBlock)); if (lowestBlockY.isPresent()) { heightMap[x][z] = lowestBlockY.getAsInt(); } else { missingColumns++; heightMap[x][z] = Integer.MAX_VALUE; } } } if (missingColumns != 0) { System.out.println(missingColumns + " columns had no block despite being in a map art, letting them be whatever"); } return heightMap; } private static <T> OptionalInt lastIndexMatching(T[] arr, Predicate<? super T> predicate) { for (int y = arr.length - 1; y >= 0; y--) { if (predicate.test(arr[y])) { return OptionalInt.of(y); } } return OptionalInt.empty(); } } ================================================ FILE: src/main/java/baritone/utils/schematic/SchematicSystem.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic; import baritone.api.command.registry.Registry; import baritone.api.schematic.ISchematicSystem; import baritone.api.schematic.format.ISchematicFormat; import baritone.utils.schematic.format.DefaultSchematicFormats; import java.io.File; import java.util.Arrays; import java.util.List; import java.util.Optional; /** * @author Brady * @since 12/24/2019 */ public enum SchematicSystem implements ISchematicSystem { INSTANCE; private final Registry<ISchematicFormat> registry = new Registry<>(); SchematicSystem() { Arrays.stream(DefaultSchematicFormats.values()).forEach(this.registry::register); } @Override public Registry<ISchematicFormat> getRegistry() { return this.registry; } @Override public Optional<ISchematicFormat> getByFile(File file) { return this.registry.stream().filter(format -> format.isFileType(file)).findFirst(); } @Override public List<String> getFileExtensions() { return this.registry.stream().map(ISchematicFormat::getFileExtensions).flatMap(List::stream).toList(); } } ================================================ FILE: src/main/java/baritone/utils/schematic/SelectionSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic; import baritone.api.schematic.ISchematic; import baritone.api.schematic.MaskSchematic; import baritone.api.selection.ISelection; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.world.level.block.state.BlockState; import java.util.stream.Stream; public class SelectionSchematic extends MaskSchematic { private final ISelection[] selections; public SelectionSchematic(ISchematic schematic, Vec3i origin, ISelection[] selections) { super(schematic); this.selections = Stream.of(selections).map( sel -> sel .shift(Direction.WEST, origin.getX()) .shift(Direction.DOWN, origin.getY()) .shift(Direction.NORTH, origin.getZ())) .toArray(ISelection[]::new); } @Override protected boolean partOfMask(int x, int y, int z, BlockState currentState) { for (ISelection selection : selections) { if (x >= selection.min().x && y >= selection.min().y && z >= selection.min().z && x <= selection.max().x && y <= selection.max().y && z <= selection.max().z) { return true; } } return false; } } ================================================ FILE: src/main/java/baritone/utils/schematic/StaticSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic; import baritone.api.schematic.AbstractSchematic; import baritone.api.schematic.IStaticSchematic; import java.util.List; import net.minecraft.world.level.block.state.BlockState; /** * Default implementation of {@link IStaticSchematic} * * @author Brady * @since 12/23/2019 */ public class StaticSchematic extends AbstractSchematic implements IStaticSchematic { protected BlockState[][][] states; public StaticSchematic() {} public StaticSchematic(BlockState[][][] states) { this.states = states; boolean empty = states.length == 0 || states[0].length == 0 || states[0][0].length == 0; this.x = empty ? 0 : states.length; this.z = empty ? 0 : states[0].length; this.y = empty ? 0 : states[0][0].length; } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { return this.states[x][z][y]; } @Override public BlockState getDirect(int x, int y, int z) { return this.states[x][z][y]; } @Override public BlockState[] getColumn(int x, int z) { return this.states[x][z]; } } ================================================ FILE: src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic.format; import baritone.api.schematic.IStaticSchematic; import baritone.api.schematic.format.ISchematicFormat; import baritone.utils.schematic.format.defaults.LitematicaSchematic; import baritone.utils.schematic.format.defaults.MCEditSchematic; import baritone.utils.schematic.format.defaults.SpongeSchematic; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; import org.apache.commons.io.FilenameUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.List; /** * Default implementations of {@link ISchematicFormat} * * @author Brady * @since 12/13/2019 */ public enum DefaultSchematicFormats implements ISchematicFormat { /** * The MCEdit schematic specification. Commonly denoted by the ".schematic" file extension. */ MCEDIT("schematic") { @Override public IStaticSchematic parse(InputStream input) throws IOException { return new MCEditSchematic(NbtIo.readCompressed(input)); } }, /** * The SpongePowered Schematic Specification. Commonly denoted by the ".schem" file extension. * * @see <a href="https://github.com/SpongePowered/Schematic-Specification">Sponge Schematic Specification</a> */ SPONGE("schem") { @Override public IStaticSchematic parse(InputStream input) throws IOException { CompoundTag nbt = NbtIo.readCompressed(input); int version = nbt.getInt("Version"); switch (version) { case 1: case 2: return new SpongeSchematic(nbt); default: throw new UnsupportedOperationException("Unsupported Version of a Sponge Schematic"); } } }, /** * The Litematica schematic specification. Commonly denoted by the ".litematic" file extension. */ LITEMATICA("litematic") { @Override public IStaticSchematic parse(InputStream input) throws IOException { CompoundTag nbt = NbtIo.readCompressed(input); int version = nbt.getInt("Version"); switch (version) { case 4: //1.12 case 5: //1.13-1.17 throw new UnsupportedOperationException("This litematic Version is too old."); case 6: //1.18+ return new LitematicaSchematic(nbt); default: throw new UnsupportedOperationException("Unsuported Version of a Litematica Schematic"); } } }; private final String extension; DefaultSchematicFormats(String extension) { this.extension = extension; } @Override public boolean isFileType(File file) { return this.extension.equalsIgnoreCase(FilenameUtils.getExtension(file.getAbsolutePath())); } @Override public List<String> getFileExtensions() { return Collections.singletonList(this.extension); } } ================================================ FILE: src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic.format.defaults; import baritone.api.schematic.CompositeSchematic; import baritone.api.schematic.IStaticSchematic; import baritone.utils.schematic.StaticSchematic; import net.minecraft.core.Vec3i; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; import org.apache.commons.lang3.Validate; import javax.annotation.Nullable; import java.util.Collections; import java.util.Optional; /** * Based on EmersonDove's work * <a href="https://github.com/cabaletta/baritone/pull/2544">...</a> * * @author rycbar * @since 22.09.2022 */ public final class LitematicaSchematic extends CompositeSchematic implements IStaticSchematic { /** * @param nbtTagCompound a decompressed file stream aka nbt data. * @param rotated if the schematic is rotated by 90°. */ public LitematicaSchematic(CompoundTag nbt) { super(0, 0, 0); fillInSchematic(nbt); } /** * @return Array of subregion tags. */ private static CompoundTag[] getRegions(CompoundTag nbt) { return nbt.getCompound("Regions").getAllKeys().stream() .map(nbt.getCompound("Regions")::getCompound) .toArray(CompoundTag[]::new); } /** * Gets both ends from a region box for a given axis and returns the lower one. * * @param s axis that should be read. * @return the lower coord of the requested axis. */ private static int getMinOfSubregion(CompoundTag subReg, String s) { int a = subReg.getCompound("Position").getInt(s); int b = subReg.getCompound("Size").getInt(s); return Math.min(a, a + b + 1); } /** * @param blockStatePalette List of all different block types used in the schematic. * @return Array of BlockStates. */ private static BlockState[] getBlockList(ListTag blockStatePalette) { BlockState[] blockList = new BlockState[blockStatePalette.size()]; for (int i = 0; i < blockStatePalette.size(); i++) { Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation((((CompoundTag) blockStatePalette.get(i)).getString("Name")))); CompoundTag properties = ((CompoundTag) blockStatePalette.get(i)).getCompound("Properties"); blockList[i] = getBlockState(block, properties); } return blockList; } /** * @param block block. * @param properties List of Properties the block has. * @return A blockState. */ private static BlockState getBlockState(Block block, CompoundTag properties) { BlockState blockState = block.defaultBlockState(); for (Object key : properties.getAllKeys()) { Property<?> property = block.getStateDefinition().getProperty((String) key); String propertyValue = properties.getString((String) key); if (property != null) { blockState = setPropertyValue(blockState, property, propertyValue); } } return blockState; } /** * @author Emerson */ private static <T extends Comparable<T>> BlockState setPropertyValue(BlockState state, Property<T> property, String value) { Optional<T> parsed = property.getValue(value); if (parsed.isPresent()) { return state.setValue(property, parsed.get()); } else { throw new IllegalArgumentException("Invalid value for property " + property); } } /** * @param amountOfBlockTypes amount of block types in the schematic. * @return amount of bits used to encode a block. */ private static int getBitsPerBlock(int amountOfBlockTypes) { return (int) Math.max(2, Math.ceil(Math.log(amountOfBlockTypes) / Math.log(2))); } /** * Calculates the volume of the subregion. As size can be a negative value we take the absolute value of the * multiplication as the volume still holds a positive amount of blocks. * * @return the volume of the subregion. */ private static long getVolume(CompoundTag subReg) { CompoundTag size = subReg.getCompound("Size"); return Math.abs(size.getInt("x") * size.getInt("y") * size.getInt("z")); } /** * @param s axis. * @return the lowest coordinate of that axis of the schematic. */ private static int getMinOfSchematic(CompoundTag nbt, String s) { int n = Integer.MAX_VALUE; for (CompoundTag subReg : getRegions(nbt)) { n = Math.min(n, getMinOfSubregion(subReg, s)); } return n; } /** * reads the file data. */ private void fillInSchematic(CompoundTag nbt) { Vec3i offsetMinCorner = new Vec3i(getMinOfSchematic(nbt, "x"), getMinOfSchematic(nbt, "y"), getMinOfSchematic(nbt, "z")); for (CompoundTag subReg : getRegions(nbt)) { ListTag usedBlockTypes = subReg.getList("BlockStatePalette", 10); BlockState[] blockList = getBlockList(usedBlockTypes); int bitsPerBlock = getBitsPerBlock(usedBlockTypes.size()); long regionVolume = getVolume(subReg); long[] blockStateArray = subReg.getLongArray("BlockStates"); LitematicaBitArray bitArray = new LitematicaBitArray(bitsPerBlock, regionVolume, blockStateArray); writeSubregionIntoSchematic(subReg, offsetMinCorner, blockList, bitArray); } } /** * Writes the file data in to the IBlockstate array. * * @param blockList list with the different block types used in the schematic. * @param bitArray bit array that holds the placement pattern. */ private void writeSubregionIntoSchematic(CompoundTag subReg, Vec3i offsetMinCorner, BlockState[] blockList, LitematicaBitArray bitArray) { int offsetX = getMinOfSubregion(subReg, "x") - offsetMinCorner.getX(); int offsetY = getMinOfSubregion(subReg, "y") - offsetMinCorner.getY(); int offsetZ = getMinOfSubregion(subReg, "z") - offsetMinCorner.getZ(); CompoundTag size = subReg.getCompound("Size"); int sizeX = Math.abs(size.getInt("x")); int sizeY = Math.abs(size.getInt("y")); int sizeZ = Math.abs(size.getInt("z")); BlockState[][][] states = new BlockState[sizeX][sizeZ][sizeY]; int index = 0; for (int y = 0; y < sizeY; y++) { for (int z = 0; z < sizeZ; z++) { for (int x = 0; x < sizeX; x++) { states[x][z][y] = blockList[bitArray.getAt(index)]; index++; } } } this.put(new StaticSchematic(states), offsetX, offsetY, offsetZ); } @Override public BlockState getDirect(int x, int y, int z) { return desiredState(x, y, z, null, Collections.emptyList()); } /** * @author maruohon * Class from the Litematica mod by maruohon * Usage under LGPLv3 with the permission of the author. * <a href="https://github.com/maruohon/litematica">...</a> */ private static class LitematicaBitArray { /** * The long array that is used to store the data for this BitArray. */ private final long[] longArray; /** * Number of bits a single entry takes up */ private final int bitsPerEntry; /** * The maximum value for a single entry. This also works as a bitmask for a single entry. * For instance, if bitsPerEntry were 5, this value would be 31 (ie, {@code 0b00011111}). */ private final long maxEntryValue; /** * Number of entries in this array (<b>not</b> the length of the long array that internally backs this array) */ private final long arraySize; public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn, @Nullable long[] longArrayIn) { Validate.inclusiveBetween(1L, 32L, bitsPerEntryIn); this.arraySize = arraySizeIn; this.bitsPerEntry = bitsPerEntryIn; this.maxEntryValue = (1L << bitsPerEntryIn) - 1L; if (longArrayIn != null) { this.longArray = longArrayIn; } else { this.longArray = new long[(int) (roundUp(arraySizeIn * (long) bitsPerEntryIn, 64L) / 64L)]; } } public static long roundUp(long number, long interval) { int sign = 1; if (interval == 0) { return 0; } else if (number == 0) { return interval; } else { if (number < 0) { sign = -1; } long i = number % (interval * sign); return i == 0 ? number : number + (interval * sign) - i; } } public int getAt(long index) { Validate.inclusiveBetween(0L, this.arraySize - 1L, index); long startOffset = index * (long) this.bitsPerEntry; int startArrIndex = (int) (startOffset >> 6); // startOffset / 64 int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6); int startBitOffset = (int) (startOffset & 0x3F); // startOffset % 64 if (startArrIndex == endArrIndex) { return (int) (this.longArray[startArrIndex] >>> startBitOffset & this.maxEntryValue); } else { int endOffset = 64 - startBitOffset; return (int) ((this.longArray[startArrIndex] >>> startBitOffset | this.longArray[endArrIndex] << endOffset) & this.maxEntryValue); } } public long size() { return this.arraySize; } } } ================================================ FILE: src/main/java/baritone/utils/schematic/format/defaults/MCEditSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic.format.defaults; import baritone.utils.schematic.StaticSchematic; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.datafix.fixes.ItemIdFix; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; /** * @author Brady * @since 12/27/2019 */ public final class MCEditSchematic extends StaticSchematic { public MCEditSchematic(CompoundTag schematic) { String type = schematic.getString("Materials"); if (!type.equals("Alpha")) { throw new IllegalStateException("bad schematic " + type); } this.x = schematic.getInt("Width"); this.y = schematic.getInt("Height"); this.z = schematic.getInt("Length"); byte[] blocks = schematic.getByteArray("Blocks"); // byte[] metadata = schematic.getByteArray("Data"); byte[] additional = null; if (schematic.contains("AddBlocks")) { byte[] addBlocks = schematic.getByteArray("AddBlocks"); additional = new byte[addBlocks.length * 2]; for (int i = 0; i < addBlocks.length; i++) { additional[i * 2 + 0] = (byte) ((addBlocks[i] >> 4) & 0xF); // lower nibble additional[i * 2 + 1] = (byte) ((addBlocks[i] >> 0) & 0xF); // upper nibble } } this.states = new BlockState[this.x][this.z][this.y]; for (int y = 0; y < this.y; y++) { for (int z = 0; z < this.z; z++) { for (int x = 0; x < this.x; x++) { int blockInd = (y * this.z + z) * this.x + x; int blockID = blocks[blockInd] & 0xFF; if (additional != null) { // additional is 0 through 15 inclusive since it's & 0xF above blockID |= additional[blockInd] << 8; } Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.tryParse(ItemIdFix.getItem(blockID))); // int meta = metadata[blockInd] & 0xFF; // this.states[x][z][y] = block.getStateFromMeta(meta); this.states[x][z][y] = block.defaultBlockState(); } } } } } ================================================ FILE: src/main/java/baritone/utils/schematic/format/defaults/SpongeSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic.format.defaults; import baritone.utils.schematic.StaticSchematic; import baritone.utils.type.VarInt; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; /** * @author Brady * @since 12/27/2019 */ public final class SpongeSchematic extends StaticSchematic { public SpongeSchematic(CompoundTag nbt) { this.x = nbt.getInt("Width"); this.y = nbt.getInt("Height"); this.z = nbt.getInt("Length"); this.states = new BlockState[this.x][this.z][this.y]; Int2ObjectArrayMap<BlockState> palette = new Int2ObjectArrayMap<>(); CompoundTag paletteTag = nbt.getCompound("Palette"); for (String tag : paletteTag.getAllKeys()) { int index = paletteTag.getInt(tag); SerializedBlockState serializedState = SerializedBlockState.getFromString(tag); if (serializedState == null) { throw new IllegalArgumentException("Unable to parse palette tag"); } BlockState state = serializedState.deserialize(); if (state == null) { throw new IllegalArgumentException("Unable to deserialize palette tag"); } palette.put(index, state); } // BlockData is stored as an NBT byte[], however, the actual data that is represented is a varint[] byte[] rawBlockData = nbt.getByteArray("BlockData"); int[] blockData = new int[this.x * this.y * this.z]; int offset = 0; for (int i = 0; i < blockData.length; i++) { if (offset >= rawBlockData.length) { throw new IllegalArgumentException("No remaining bytes in BlockData for complete schematic"); } VarInt varInt = VarInt.read(rawBlockData, offset); blockData[i] = varInt.getValue(); offset += varInt.getSize(); } for (int y = 0; y < this.y; y++) { for (int z = 0; z < this.z; z++) { for (int x = 0; x < this.x; x++) { int index = (y * this.z + z) * this.x + x; BlockState state = palette.get(blockData[index]); if (state == null) { throw new IllegalArgumentException("Invalid Palette Index " + index); } this.states[x][z][y] = state; } } } } private static final class SerializedBlockState { private static final Pattern REGEX = Pattern.compile("(?<location>(\\w+:)?\\w+)(\\[(?<properties>(\\w+=\\w+,?)+)])?"); private final ResourceLocation resourceLocation; private final Map<String, String> properties; private BlockState blockState; private SerializedBlockState(ResourceLocation resourceLocation, Map<String, String> properties) { this.resourceLocation = resourceLocation; this.properties = properties; } private BlockState deserialize() { if (this.blockState == null) { Block block = BuiltInRegistries.BLOCK.get(this.resourceLocation); this.blockState = block.defaultBlockState(); this.properties.keySet().stream().sorted(String::compareTo).forEachOrdered(key -> { Property<?> property = block.getStateDefinition().getProperty(key); if (property != null) { this.blockState = setPropertyValue(this.blockState, property, this.properties.get(key)); } }); } return this.blockState; } private static SerializedBlockState getFromString(String s) { Matcher m = REGEX.matcher(s); if (!m.matches()) { return null; } try { String location = m.group("location"); String properties = m.group("properties"); ResourceLocation resourceLocation = new ResourceLocation(location); Map<String, String> propertiesMap = new HashMap<>(); if (properties != null) { for (String property : properties.split(",")) { String[] split = property.split("="); propertiesMap.put(split[0], split[1]); } } return new SerializedBlockState(resourceLocation, propertiesMap); } catch (Exception e) { e.printStackTrace(); return null; } } private static <T extends Comparable<T>> BlockState setPropertyValue(BlockState state, Property<T> property, String value) { Optional<T> parsed = property.getValue(value); if (parsed.isPresent()) { return state.setValue(property, parsed.get()); } else { throw new IllegalArgumentException("Invalid value for property " + property); } } } } ================================================ FILE: src/main/java/baritone/utils/schematic/litematica/LitematicaHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic.litematica; import baritone.api.schematic.CompositeSchematic; import baritone.api.schematic.IStaticSchematic; import baritone.utils.schematic.StaticSchematic; import fi.dy.masa.litematica.Litematica; import fi.dy.masa.litematica.data.DataManager; import fi.dy.masa.litematica.schematic.placement.SchematicPlacement; import fi.dy.masa.litematica.schematic.placement.SubRegionPlacement; import fi.dy.masa.litematica.world.SchematicWorldHandler; import fi.dy.masa.litematica.world.WorldSchematic; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.util.Tuple; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Helper class that provides access or processes data related to Litmatica schematics. * * @author rycbar * @since 28.09.2022 */ public final class LitematicaHelper { /** * @return if Litmatica is installed. */ public static boolean isLitematicaPresent() { try { Class.forName(Litematica.class.getName()); return true; } catch (ClassNotFoundException | NoClassDefFoundError ex) { return false; } } /** * @return if {@code i} is a valid placement index */ public static boolean hasLoadedSchematic(int i) { return 0 <= i && i < DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().size(); } private static SchematicPlacement getPlacement(int i) { return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i); } private static Vec3i transform(Vec3i in, Mirror mirror, Rotation rotation) { int x = in.getX(); int z = in.getZ(); if (mirror == Mirror.LEFT_RIGHT) { z = -z; } else if (mirror == Mirror.FRONT_BACK) { x = -x; } switch (rotation) { case CLOCKWISE_90: return new Vec3i(-z, in.getY(), x); case CLOCKWISE_180: return new Vec3i(-x, in.getY(), -z); case COUNTERCLOCKWISE_90: return new Vec3i(z, in.getY(), -x); default: return new Vec3i(x, in.getY(), z); } } /** * @param i index of the Schematic in the schematic placement list. * @return The transformed schematic and the position of its minimum corner */ public static Tuple<IStaticSchematic, Vec3i> getSchematic(int i) { SchematicPlacement placement = getPlacement(i); int minX = Integer.MAX_VALUE; int minY = Integer.MAX_VALUE; int minZ = Integer.MAX_VALUE; HashMap<Vec3i, StaticSchematic> subRegions = new HashMap<>(); Level schematicWorld = SchematicWorldHandler.getSchematicWorld(); for (Map.Entry<String, SubRegionPlacement> entry : placement.getEnabledRelativeSubRegionPlacements().entrySet()) { SubRegionPlacement subPlacement = entry.getValue(); Vec3i pos = transform(subPlacement.getPos(), placement.getMirror(), placement.getRotation()); Vec3i size = placement.getSchematic().getAreaSize(entry.getKey()); size = transform(size, placement.getMirror(), placement.getRotation()); size = transform(size, subPlacement.getMirror(), subPlacement.getRotation()); int mx = Math.min(size.getX() + 1, 0); int my = Math.min(size.getY() + 1, 0); int mz = Math.min(size.getZ() + 1, 0); minX = Math.min(minX, pos.getX() + mx); minY = Math.min(minY, pos.getY() + my); minZ = Math.min(minZ, pos.getZ() + mz); BlockPos origin = placement.getOrigin().offset(pos).offset(mx, my, mz); BlockState[][][] states = new BlockState[Math.abs(size.getX())][Math.abs(size.getZ())][Math.abs(size.getY())]; for (int x = 0; x < states.length; x++) { for (int z = 0; z < states[x].length; z++) { for (int y = 0; y < states[x][z].length; y++) { states[x][z][y] = schematicWorld.getBlockState(origin.offset(x, y, z)); } } } StaticSchematic schematic = new StaticSchematic(states); subRegions.put(pos.offset(mx, my, mz), schematic); } LitematicaPlacementSchematic composite = new LitematicaPlacementSchematic(placement.getName()); for (Map.Entry<Vec3i, StaticSchematic> entry : subRegions.entrySet()) { Vec3i pos = entry.getKey().offset(-minX, -minY, -minZ); composite.put(entry.getValue(), pos.getX(), pos.getY(), pos.getZ()); } return new Tuple<>(composite, placement.getOrigin().offset(minX, minY, minZ)); } private static class LitematicaPlacementSchematic extends CompositeSchematic implements IStaticSchematic { private final String name; public LitematicaPlacementSchematic(String name) { super(0, 0, 0); this.name = name; } @Override public BlockState getDirect(int x, int y, int z) { if (inSchematic(x, y, z, null)) { return desiredState(x, y, z, null, Collections.emptyList()); } return null; } @Override public String toString() { return name; } } } ================================================ FILE: src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic.schematica; import baritone.api.schematic.IStaticSchematic; import com.github.lunatrius.schematica.client.world.SchematicWorld; import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; public final class SchematicAdapter implements IStaticSchematic { private final SchematicWorld schematic; public SchematicAdapter(SchematicWorld schematicWorld) { this.schematic = schematicWorld; } @Override public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) { return this.getDirect(x, y, z); } @Override public BlockState getDirect(int x, int y, int z) { return this.schematic.getSchematic().getBlockState(new BlockPos(x, y, z)); } @Override public int widthX() { return schematic.getSchematic().getWidth(); } @Override public int heightY() { return schematic.getSchematic().getHeight(); } @Override public int lengthZ() { return schematic.getSchematic().getLength(); } } ================================================ FILE: src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.schematic.schematica; import baritone.api.schematic.IStaticSchematic; import com.github.lunatrius.schematica.Schematica; import com.github.lunatrius.schematica.proxy.ClientProxy; import net.minecraft.core.BlockPos; import net.minecraft.util.Tuple; import java.util.Optional; public enum SchematicaHelper { ; public static boolean isSchematicaPresent() { try { Class.forName(Schematica.class.getName()); return true; } catch (ClassNotFoundException | NoClassDefFoundError ex) { return false; } } public static Optional<Tuple<IStaticSchematic, BlockPos>> getOpenSchematic() { return Optional.ofNullable(ClientProxy.schematic) .map(world -> new Tuple<>(new SchematicAdapter(world), world.position)); } } ================================================ FILE: src/main/java/baritone/utils/type/VarInt.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.type; import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteList; /** * @author Brady * @since 12/19/2019 */ public final class VarInt { private final int value; private final byte[] serialized; private final int size; public VarInt(int value) { this.value = value; this.serialized = serialize0(this.value); this.size = this.serialized.length; } /** * @return The integer value that is represented by this {@link VarInt}. */ public final int getValue() { return this.value; } /** * @return The size of this {@link VarInt}, in bytes, once serialized. */ public final int getSize() { return this.size; } public final byte[] serialize() { return this.serialized; } private static byte[] serialize0(int valueIn) { ByteList bytes = new ByteArrayList(); int value = valueIn; while ((value & 0x80) != 0) { bytes.add((byte) (value & 0x7F | 0x80)); value >>>= 7; } bytes.add((byte) (value & 0xFF)); return bytes.toByteArray(); } public static VarInt read(byte[] bytes) { return read(bytes, 0); } public static VarInt read(byte[] bytes, int start) { int value = 0; int size = 0; int index = start; while (true) { byte b = bytes[index++]; value |= (b & 0x7F) << size++ * 7; if (size > 5) { throw new IllegalArgumentException("VarInt size cannot exceed 5 bytes"); } // Most significant bit denotes another byte is to be read. if ((b & 0x80) == 0) { break; } } return new VarInt(value); } } ================================================ FILE: src/schematica_api/java/com/github/lunatrius/core/util/math/MBlockPos.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package com.github.lunatrius.core.util.math; import net.minecraft.core.BlockPos; public class MBlockPos extends BlockPos { MBlockPos() { super(6, 6, 6); } @Override public int getX() { throw new LinkageError("LOL"); } @Override public int getY() { throw new LinkageError("LOL"); } @Override public int getZ() { throw new LinkageError("LOL"); } } ================================================ FILE: src/schematica_api/java/com/github/lunatrius/schematica/Schematica.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package com.github.lunatrius.schematica; import com.github.lunatrius.schematica.proxy.CommonProxy; public class Schematica { public static CommonProxy proxy; } ================================================ FILE: src/schematica_api/java/com/github/lunatrius/schematica/api/ISchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package com.github.lunatrius.schematica.api; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; public interface ISchematic { BlockState getBlockState(BlockPos var1); int getWidth(); int getHeight(); int getLength(); } ================================================ FILE: src/schematica_api/java/com/github/lunatrius/schematica/client/world/SchematicWorld.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package com.github.lunatrius.schematica.client.world; import com.github.lunatrius.core.util.math.MBlockPos; import com.github.lunatrius.schematica.api.ISchematic; public class SchematicWorld { public final MBlockPos position = (MBlockPos) (Object) "cringe"; public ISchematic getSchematic() { throw new LinkageError("LOL"); } } ================================================ FILE: src/schematica_api/java/com/github/lunatrius/schematica/proxy/ClientProxy.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package com.github.lunatrius.schematica.proxy; import com.github.lunatrius.schematica.client.world.SchematicWorld; public class ClientProxy extends CommonProxy { public static SchematicWorld schematic; } ================================================ FILE: src/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package com.github.lunatrius.schematica.proxy; public abstract class CommonProxy {} ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/Litematica.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica; public class Litematica { } ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/data/DataManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica.data; import fi.dy.masa.litematica.schematic.placement.SchematicPlacementManager; public class DataManager { public static SchematicPlacementManager getSchematicPlacementManager() { throw new LinkageError(); } } ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/schematic/LitematicaSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica.schematic; import net.minecraft.core.BlockPos; public class LitematicaSchematic { public BlockPos getAreaSize(String name) { throw new LinkageError(); } } ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacement.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica.schematic.placement; import com.google.common.collect.ImmutableMap; import fi.dy.masa.litematica.schematic.LitematicaSchematic; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; public class SchematicPlacement { public String getName() { throw new LinkageError(); } public BlockPos getOrigin() { throw new LinkageError(); } public Rotation getRotation() { throw new LinkageError(); } public Mirror getMirror() { throw new LinkageError(); } public ImmutableMap<String, SubRegionPlacement> getEnabledRelativeSubRegionPlacements() { throw new LinkageError(); } public LitematicaSchematic getSchematic() { throw new LinkageError(); } } ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementManager.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica.schematic.placement; import java.util.List; public class SchematicPlacementManager { //in case of a java.lang.NoSuchMethodError try change the name of this method to getAllSchematicPlacements() //there are inconsistencies in the litematica mod about the naming of this method public List<SchematicPlacement> getAllSchematicsPlacements() { throw new LinkageError(); } } ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SubRegionPlacement.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica.schematic.placement; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; public class SubRegionPlacement { public BlockPos getPos() { throw new LinkageError(); } public Rotation getRotation() { throw new LinkageError(); } public Mirror getMirror() { throw new LinkageError(); } } ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/world/SchematicWorldHandler.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica.world; public class SchematicWorldHandler { public static WorldSchematic getSchematicWorld() { throw new LinkageError(); } } ================================================ FILE: src/schematica_api/java/fi/dy/masa/litematica/world/WorldSchematic.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package fi.dy.masa.litematica.world; import net.minecraft.world.level.Level; public abstract class WorldSchematic extends Level { private WorldSchematic() { super(null, null, null, null, null, false, false, 0, 0); throw new LinkageError(); } } ================================================ FILE: src/test/java/baritone/cache/CachedRegionTest.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.cache; import org.junit.Test; import static junit.framework.TestCase.assertEquals; public class CachedRegionTest { @Test public void blockPosSaving() { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < 256; y++) { byte part1 = (byte) (z << 4 | x); byte part2 = (byte) (y); byte xz = part1; int X = xz & 0x0f; int Z = (xz >>> 4) & 0x0f; int Y = part2 & 0xff; if (x != X || y != Y || z != Z) { System.out.println(x + " " + X + " " + y + " " + Y + " " + z + " " + Z); } assertEquals(x, X); assertEquals(y, Y); assertEquals(z, Z); } } } } } ================================================ FILE: src/test/java/baritone/pathing/calc/openset/OpenSetsTest.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.calc.openset; import baritone.api.pathing.goals.Goal; import baritone.pathing.calc.PathNode; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.*; import static org.junit.Assert.*; @RunWith(Parameterized.class) public class OpenSetsTest { private final int size; public OpenSetsTest(int size) { this.size = size; } @Parameterized.Parameters public static Collection<Object[]> data() { ArrayList<Object[]> testSizes = new ArrayList<>(); for (int size = 1; size < 20; size++) { testSizes.add(new Object[]{size}); } for (int size = 100; size <= 1000; size += 100) { testSizes.add(new Object[]{size}); } testSizes.add(new Object[]{5000}); testSizes.add(new Object[]{10000}); return testSizes; } private static void removeAndTest(int amount, IOpenSet[] test, Collection<PathNode> mustContain) { double[][] results = new double[test.length][amount]; for (int i = 0; i < test.length; i++) { long before = System.nanoTime() / 1000000L; for (int j = 0; j < amount; j++) { PathNode pn = test[i].removeLowest(); if (mustContain != null && !mustContain.contains(pn)) { throw new IllegalStateException(mustContain + " " + pn); } results[i][j] = pn.combinedCost; } System.out.println(test[i].getClass() + " " + (System.nanoTime() / 1000000L - before)); } for (int j = 0; j < amount; j++) { for (int i = 1; i < test.length; i++) { assertEquals(results[i][j], results[0][j], 0); } } for (int i = 0; i < amount - 1; i++) { assertTrue(results[0][i] < results[0][i + 1]); } } @Test public void testSize() { System.out.println("Testing size " + size); // Include LinkedListOpenSet even though it's not performant because I absolutely trust that it behaves properly // I'm really testing the heap implementations against it as the ground truth IOpenSet[] test = new IOpenSet[]{new BinaryHeapOpenSet(), new LinkedListOpenSet()}; for (IOpenSet set : test) { assertTrue(set.isEmpty()); } // generate the pathnodes that we'll be testing the sets on PathNode[] toInsert = new PathNode[size]; for (int i = 0; i < size; i++) { // can't use an existing goal // because they use Baritone.settings() // and we can't do that because Minecraft itself isn't initted PathNode pn = new PathNode(0, 0, 0, new Goal() { @Override public boolean isInGoal(int x, int y, int z) { return false; } @Override public double heuristic(int x, int y, int z) { return 0; } }); pn.combinedCost = Math.random(); toInsert[i] = pn; } // create a list of what the first removals should be ArrayList<PathNode> copy = new ArrayList<>(Arrays.asList(toInsert)); copy.sort(Comparator.comparingDouble(pn -> pn.combinedCost)); Set<PathNode> lowestQuarter = new HashSet<>(copy.subList(0, size / 4)); // all opensets should be empty; nothing has been inserted yet for (IOpenSet set : test) { assertTrue(set.isEmpty()); } System.out.println("Insertion"); for (IOpenSet set : test) { long before = System.nanoTime() / 1000000L; for (int i = 0; i < size; i++) set.insert(toInsert[i]); System.out.println(set.getClass() + " " + (System.nanoTime() / 1000000L - before)); //all three take either 0 or 1ms to insert up to 10,000 nodes //linkedlist takes 0ms most often (because there's no array resizing or allocation there, just pointer shuffling) } // all opensets should now be full for (IOpenSet set : test) { assertFalse(set.isEmpty()); } System.out.println("Removal round 1"); // remove a quarter of the nodes and verify that they are indeed the size/4 lowest ones removeAndTest(size / 4, test, lowestQuarter); // none of them should be empty (sanity check) for (IOpenSet set : test) { assertFalse(set.isEmpty()); } int cnt = 0; for (int i = 0; cnt < size / 2 && i < size; i++) { if (lowestQuarter.contains(toInsert[i])) { // these were already removed and can't be updated to test continue; } toInsert[i].combinedCost *= Math.random(); // multiplying it by a random number between 0 and 1 is guaranteed to decrease it for (IOpenSet set : test) { // it's difficult to benchmark these individually because if you modify all at once then update then // it breaks the internal consistency of the heaps. // you have to call update every time you modify a node. set.update(toInsert[i]); } cnt++; } //still shouldn't be empty for (IOpenSet set : test) { assertFalse(set.isEmpty()); } System.out.println("Removal round 2"); // remove the remaining 3/4 removeAndTest(size - size / 4, test, null); // every set should now be empty for (IOpenSet set : test) { assertTrue(set.isEmpty()); } } } ================================================ FILE: src/test/java/baritone/pathing/goals/GoalGetToBlockTest.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.goals; import baritone.api.pathing.goals.GoalGetToBlock; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import net.minecraft.core.BlockPos; import static org.junit.Assert.assertTrue; public class GoalGetToBlockTest { @Test public void isInGoal() { List<String> acceptableOffsets = new ArrayList<>(Arrays.asList("0,0,0", "0,0,1", "0,0,-1", "1,0,0", "-1,0,0", "0,-1,1", "0,-1,-1", "1,-1,0", "-1,-1,0", "0,1,0", "0,-1,0", "0,-2,0")); for (int x = -10; x <= 10; x++) { for (int y = -10; y <= 10; y++) { for (int z = -10; z <= 10; z++) { boolean inGoal = new GoalGetToBlock(new BlockPos(0, 0, 0)).isInGoal(new BlockPos(x, y, z)); String repr = x + "," + y + "," + z; System.out.println(repr + " " + inGoal); if (inGoal) { assertTrue(repr, acceptableOffsets.contains(repr)); acceptableOffsets.remove(repr); } } } } assertTrue(acceptableOffsets.toString(), acceptableOffsets.isEmpty()); } } ================================================ FILE: src/test/java/baritone/pathing/movement/ActionCostsTest.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.pathing.movement; import org.junit.Test; import static baritone.api.pathing.movement.ActionCosts.*; import static org.junit.Assert.assertEquals; public class ActionCostsTest { @Test public void testFallNBlocksCost() { assertEquals(FALL_N_BLOCKS_COST.length, 4097); // Fall 0 blocks through fall 4096 blocks for (int i = 0; i < 4097; i++) { double blocks = ticksToBlocks(FALL_N_BLOCKS_COST[i]); assertEquals(blocks, i, 0.00000000001); // If you add another 0 the test fails at i=989 LOL } assertEquals(FALL_1_25_BLOCKS_COST, 6.2344, 0.00001); assertEquals(FALL_0_25_BLOCKS_COST, 3.0710, 0.00001); assertEquals(JUMP_ONE_BLOCK_COST, 3.1634, 0.00001); } public double ticksToBlocks(double ticks) { double fallDistance = 0; int integralComponent = (int) Math.floor(ticks); for (int tick = 0; tick < integralComponent; tick++) { fallDistance += velocity(tick); } double partialTickComponent = ticks - Math.floor(ticks); double finalPartialTickVelocity = velocity(integralComponent); double finalPartialTickDistance = finalPartialTickVelocity * partialTickComponent; fallDistance += finalPartialTickDistance; return fallDistance; } } ================================================ FILE: src/test/java/baritone/utils/pathing/BetterBlockPosTest.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; import baritone.api.utils.BetterBlockPos; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class BetterBlockPosTest { // disabled since this is a benchmark, not really a test. also including it makes the tests take 50 seconds for no reason /*@Test public void benchMulti() { System.out.println("Benching up()"); for (int i = 0; i < 10; i++) { // eliminate any advantage to going first benchOne(); } System.out.println("Benching up(int)"); for (int i = 0; i < 10; i++) { // eliminate any advantage to going first benchN(); assertTrue(i<10); } }*/ /** * Make sure BetterBlockPos behaves just like BlockPos */ @Test public void testSimple() { BlockPos pos = new BlockPos(1, 2, 3); BetterBlockPos better = new BetterBlockPos(1, 2, 3); assertEquals(pos, better); assertEquals(pos.above(), better.above()); assertEquals(pos.below(), better.below()); assertEquals(pos.north(), better.north()); assertEquals(pos.south(), better.south()); assertEquals(pos.east(), better.east()); assertEquals(pos.west(), better.west()); for (Direction dir : Direction.values()) { assertEquals(pos.relative(dir), better.relative(dir)); assertEquals(pos.relative(dir, 0), pos); assertEquals(better.relative(dir, 0), better); for (int i = -10; i < 10; i++) { assertEquals(pos.relative(dir, i), better.relative(dir, i)); } assertTrue(better.relative(dir, 0) == better); } for (int i = -10; i < 10; i++) { assertEquals(pos.above(i), better.above(i)); assertEquals(pos.below(i), better.below(i)); assertEquals(pos.north(i), better.north(i)); assertEquals(pos.south(i), better.south(i)); assertEquals(pos.east(i), better.east(i)); assertEquals(pos.west(i), better.west(i)); } assertTrue(better.relative((Direction) null, 0) == better); } public void benchOne() { BlockPos pos = new BlockPos(1, 2, 3); BetterBlockPos pos2 = new BetterBlockPos(1, 2, 3); try { Thread.sleep(1000); // give GC some time } catch (InterruptedException e) { e.printStackTrace(); } long before1 = System.nanoTime() / 1000000L; for (int i = 0; i < 1000000; i++) { pos.above(); } long after1 = System.nanoTime() / 1000000L; try { Thread.sleep(1000); // give GC some time } catch (InterruptedException e) { e.printStackTrace(); } long before2 = System.nanoTime() / 1000000L; for (int i = 0; i < 1000000; i++) { pos2.above(); } long after2 = System.nanoTime() / 1000000L; System.out.println((after1 - before1) + " " + (after2 - before2)); } public void benchN() { BlockPos pos = new BlockPos(1, 2, 3); BetterBlockPos pos2 = new BetterBlockPos(1, 2, 3); try { Thread.sleep(1000); // give GC some time } catch (InterruptedException e) { e.printStackTrace(); } long before1 = System.nanoTime() / 1000000L; for (int i = 0; i < 1000000; i++) { pos.above(0); pos.above(1); pos.above(2); pos.above(3); pos.above(4); } long after1 = System.nanoTime() / 1000000L; try { Thread.sleep(1000); // give GC some time } catch (InterruptedException e) { e.printStackTrace(); } long before2 = System.nanoTime() / 1000000L; for (int i = 0; i < 1000000; i++) { pos2.above(0); pos2.above(1); pos2.above(2); pos2.above(3); pos2.above(4); } long after2 = System.nanoTime() / 1000000L; System.out.println((after1 - before1) + " " + (after2 - before2)); } } ================================================ FILE: src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.utils.pathing; import org.junit.Test; import static org.junit.Assert.assertTrue; public class PathingBlockTypeTest { @Test public void testBits() { for (PathingBlockType type : PathingBlockType.values()) { boolean[] bits = type.getBits(); assertTrue(type == PathingBlockType.fromBits(bits[0], bits[1])); } } } ================================================ FILE: tweaker/build.gradle ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ import baritone.gradle.task.CreateDistTask import baritone.gradle.task.ProguardTask //import baritone.gradle.task.TweakerJsonAssembler plugins { id "com.github.johnrengelman.shadow" version "8.0.0" } unimined.minecraft { runs.client = { mainClass = "net.minecraft.launchwrapper.Launch" args.addAll(["--tweakClass", "baritone.launch.tweaker.BaritoneTweaker"]) } } configurations { common shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common } dependencies { implementation "org.spongepowered:mixin:${project.mixin_version}" // for some reason mixin isn't including these... implementation "org.ow2.asm:asm:${project.asm_version}" implementation "org.ow2.asm:asm-tree:${project.asm_version}" implementation "org.ow2.asm:asm-commons:${project.asm_version}" implementation "org.ow2.asm:asm-util:${project.asm_version}" implementation "org.ow2.asm:asm-analysis:${project.asm_version}" implementation 'com.github.ImpactDevelopment:SimpleTweaker:1.2' implementation('net.minecraft:launchwrapper:of-2.3') { exclude module: 'lwjgl' exclude module: 'asm-debug-all' } compileOnly 'com.google.code.findbugs:jsr305:3.0.2' // because of multiple sourcesets `common project(":")` doesn't work for (sourceSet in rootProject.sourceSets) { if (sourceSet == rootProject.sourceSets.test) continue if (sourceSet == rootProject.sourceSets.schematica_api) continue common sourceSet.output shadowCommon sourceSet.output } } shadowJar { configurations = [project.configurations.shadowCommon] archiveClassifier.set "dev-shadow" } remapJar { inputFile.set shadowJar.archiveFile dependsOn shadowJar archiveClassifier.set null } jar { archiveClassifier.set "dev" preserveFileTimestamps = false reproducibleFileOrder = true manifest { attributes( 'MixinConfigs': 'mixins.baritone.json', "MixinConnector": "baritone.launch.BaritoneMixinConnector", 'Implementation-Title': 'Baritone', 'Implementation-Version': version, ) } } task proguard(type: ProguardTask) { proguardVersion "7.2.1" } task createDist(type: CreateDistTask, dependsOn: proguard) build.finalizedBy(createDist) publishing { publications { mavenCommon(MavenPublication) { artifactId = rootProject.archives_base_name from components.java } } // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. repositories { // Add repositories to publish to here. } } ================================================ FILE: tweaker/src/main/java/baritone/launch/LaunchTesting.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch; import com.google.common.base.Strings; import com.google.gson.GsonBuilder; import com.mojang.authlib.Agent; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.properties.PropertyMap; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication; import net.minecraft.launchwrapper.Launch; import java.io.File; import java.lang.reflect.Field; import java.net.Proxy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Based on GradleStart from ForgeGradle 2.3 * * @author Brady * @since 3/11/2019 */ public class LaunchTesting { public static void main(String[] args) { Map<String, String> arguments = new HashMap<>(); hackNatives(); arguments.put("version", "BaritownedDeveloperEnvironment"); arguments.put("assetIndex", System.getenv("assetIndex")); arguments.put("assetsDir", System.getenv().getOrDefault("assetDirectory", "assets")); arguments.put("accessToken", "FML"); arguments.put("userProperties", "{}"); arguments.put("tweakClass", System.getenv("tweakClass")); String password = System.getenv("password"); if (password != null && !password.isEmpty()) { attemptLogin(arguments, System.getenv("username"), System.getenv("password")); } List<String> argsArray = new ArrayList<>(); arguments.forEach((k, v) -> { argsArray.add("--" + k); argsArray.add(v); }); Launch.main(argsArray.toArray(new String[0])); } private static void hackNatives() { String paths = System.getProperty("java.library.path"); String nativesDir = System.getenv().get("nativesDirectory"); if (Strings.isNullOrEmpty(paths)) paths = nativesDir; else paths += File.pathSeparator + nativesDir; System.setProperty("java.library.path", paths); // hack the classloader now. try { final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths"); sysPathsField.setAccessible(true); sysPathsField.set(null, null); } catch (Throwable ignored) {} } private static void attemptLogin(Map<String, String> argMap, String username, String password) { YggdrasilUserAuthentication auth = (YggdrasilUserAuthentication) (new YggdrasilAuthenticationService(Proxy.NO_PROXY, "1")).createUserAuthentication(Agent.MINECRAFT); auth.setUsername(username); auth.setPassword(password); try { auth.logIn(); } catch (AuthenticationException var4) { throw new RuntimeException(var4); } argMap.put("accessToken", auth.getAuthenticatedToken()); argMap.put("uuid", auth.getSelectedProfile().getId().toString().replace("-", "")); argMap.put("username", auth.getSelectedProfile().getName()); argMap.put("userType", auth.getUserType().getName()); argMap.put("userProperties", (new GsonBuilder()).registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create().toJson(auth.getUserProperties())); } } ================================================ FILE: tweaker/src/main/java/baritone/launch/tweaker/BaritoneTweaker.java ================================================ /* * 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 <https://www.gnu.org/licenses/>. */ package baritone.launch.tweaker; import io.github.impactdevelopment.simpletweaker.SimpleTweaker; import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; import org.spongepowered.asm.launch.MixinBootstrap; import org.spongepowered.asm.mixin.MixinEnvironment; import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.tools.obfuscation.mcp.ObfuscationServiceMCP; import java.util.List; /** * @author Brady * @since 7/31/2018 */ public class BaritoneTweaker extends SimpleTweaker { @Override public void injectIntoClassLoader(LaunchClassLoader classLoader) { super.injectIntoClassLoader(classLoader); MixinBootstrap.init(); // noinspection unchecked List<String> tweakClasses = (List<String>) Launch.blackboard.get("TweakClasses"); String obfuscation = ObfuscationServiceMCP.NOTCH; if (tweakClasses.stream().anyMatch(s -> s.contains("net.minecraftforge.fml.common.launcher"))) { obfuscation = ObfuscationServiceMCP.SEARGE; } MixinEnvironment.getDefaultEnvironment().setSide(MixinEnvironment.Side.CLIENT); MixinEnvironment.getDefaultEnvironment().setObfuscationContext(obfuscation); Mixins.addConfiguration("mixins.baritone.json"); } }