Baritone AI pathfinder

Baritone AI pathfinder

72.7k Downloads

Placing water with #sel fill water

adamgerhant opened this issue ยท 11 comments

commented

When using "#sel fill water" it requires a water bucket for each water block. For example a line 30 blocks long requires 30 buckets of water. Is there a way to get Baritone to recognize that it only needs two buckets, and can use the infinite water created to fill any area?

For example, when placing a line of water blocks, it could place 2 water buckets 1 tile apart, then use the newly created water to place another in front, and so on.

Would it be possible to achieve this with the current version of Baritone, or could I try creating this feature?

commented

For whoever is going to pick up work on this (and I do hope I'm writing to someone here):

Current master is 174bdd2 and here's a diff of Adam's file against it
diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java
index 85be6516a..b53bd89b7 100644
--- a/src/main/java/baritone/process/BuilderProcess.java
+++ b/src/main/java/baritone/process/BuilderProcess.java
@@ -54,7 +54,9 @@
 import net.minecraft.block.properties.IProperty;
 import net.minecraft.block.state.IBlockState;
 import net.minecraft.init.Blocks;
+import net.minecraft.init.Items;
 import net.minecraft.item.ItemBlock;
+import net.minecraft.item.ItemBucket;
 import net.minecraft.item.ItemStack;
 import net.minecraft.nbt.CompressedStreamTools;
 import net.minecraft.util.EnumFacing;
@@ -369,15 +371,25 @@ private Optional<Placement> possibleToPlace(IBlockState toPlace, int x, int y, i
     private OptionalInt hasAnyItemThatWouldPlace(IBlockState desired, RayTraceResult result, Rotation rot) {
         for (int i = 0; i < 9; i++) {
             ItemStack stack = ctx.player().inventory.mainInventory.get(i);
-            if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) {
+
+            Block blockToPlace;
+            if(stack.getItem() == Items.WATER_BUCKET){
+                blockToPlace = Blocks.WATER;
+            }
+            else if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) {
                 continue;
             }
+            else{
+                blockToPlace = ((ItemBlock) stack.getItem()).getBlock();
+            }
+
             float originalYaw = ctx.player().rotationYaw;
             float originalPitch = ctx.player().rotationPitch;
             // the state depends on the facing of the player sometimes
             ctx.player().rotationYaw = rot.getYaw();
             ctx.player().rotationPitch = rot.getPitch();
-            IBlockState wouldBePlaced = ((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(
+
+            IBlockState wouldBePlaced = blockToPlace.getStateForPlacement(
                     ctx.world(),
                     result.getBlockPos().offset(result.sideHit),
                     result.sideHit,
@@ -694,7 +706,7 @@ private Goal assemble(BuilderCalculationContext bcc, List<IBlockState> approxPla
                     IBlockState desired = bcc.getSchematic(pos.x, pos.y, pos.z, state);
                     missing.put(desired, 1 + missing.getOrDefault(desired, 0));
                 }
-            } else {
+            } /*else {
                 if (state.getBlock() instanceof BlockLiquid) {
                     // 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
@@ -707,7 +719,7 @@ private Goal assemble(BuilderCalculationContext bcc, List<IBlockState> approxPla
                 } else {
                     breakable.add(pos);
                 }
-            }
+            }*/
         });
         List<Goal> toBreak = new ArrayList<>();
         breakable.forEach(pos -> toBreak.add(breakGoal(pos, bcc)));
@@ -880,14 +892,25 @@ private List<IBlockState> approxPlaceable(int size) {
         List<IBlockState> result = new ArrayList<>();
         for (int i = 0; i < size; i++) {
             ItemStack stack = ctx.player().inventory.mainInventory.get(i);
-            if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) {
+
+
+            if(stack.getItem() == Items.WATER_BUCKET){
+                result.add(Blocks.WATER.getDefaultState());
+            }
+
+            else if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) {
                 result.add(Blocks.AIR.getDefaultState());
                 continue;
             }
-            // <toxic cloud>
-            result.add(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player()));
-            // </toxic cloud>
+            else{
+                // <toxic cloud>
+                result.add(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player()));
+                // </toxic cloud>
+            }
+
+
         }
+
         return result;
     }

I don't think making PathingBehavior friends with water is required for this to be added, however some safety checks would be nice (i.e. don't place the water before placing the containing blocks and don't stand where the water will flow).
Buckets always place containedBlock.getDefaultState(), so there's no need to use getStateForPlacement on the block. To generalize from water to any liquid you can access the private field in ItemBucket using a mixin.

commented

How did you get it to place water at all? I only get "missing materials".

Anyway, this is not possible with the current version of Baritone and I wish you good luck making it work.

commented

yeah it didnt work at all. I doubt id be able to make it work but ill give it a try

commented

Do you know why baritone was never given the functionality to place water? Ive been looking through the builder process code and it seems that schematics and the BuilderProcess uses IBlockState and BetterBlockPos, which only allows for blocks to be placed, not buckets.

If not, do you know who wrote the BuilderProcess.java code? Id appreciate it if i could ask why water placing was never added.

commented

I dont think it would be possible to migrate from IBlockState to ItemStack, which would also be able to include buckets, since schematica currently only works with blocks. (Lunatrius/Schematica#260)

Currently what I am attempting to do is fill the area with a placeholder block, except when it is placed it is replaced with the water bucket item. Then, whenever bcc.get is called, if it is a water block it changes back to the placeholder block. Baritone thinks that it is placing a row of the placeholder, when it is actually water.

In general though, I think it would be really good if specific baritone features such as this could be monetized, as it would encourage a more complete and public plugin rather than just something for myself.

commented

I can't tell for sure, but here's two things that I think could be the reason:

  1. BuilderProcess is bad enough at handling liquids, no need to let it flood itself
  2. It's extra effort. Baritone currently checks all items in inventory for what they place (if at all) and buckets obviously work differently.

While the second one is just a matter of laziness, the first one is actually bad (and a way to cover number 2 ๐Ÿ˜„).

There's actually a bunch more of things needing special treatment (see #3715, half of the points is about placing things), so it would be really nice if you didn't just hardcode water placement like block placement already is.

Like many other things here BuilderProcess was written mostly by Leijurv.

commented

I don't know how you would use ItemStack instead of IBlockState. While it would certainly allow you to put a bucket in your schematic, you would loose the ability to distinguish between e.g. open and closed doors. A schematic contains blocks (in the colloquial sense, concerning code it's IBlockStates) and not every block (state) can be represented by an item (stack) (and vice versa). Water is a block like many others, it's just the only one that does not have a BlockItem (or ItemBlock, not sure anymore) despite commonly being placed by players and it is the one with the most complex behavior.

I'm not quite sure what you are doing here, though as I said, water is just a block like any other so I don't see a need to pass placeholders instead. It's its own placeholder if you want. If the problem is getting Baritone to select the correct slot, then the place to look at is the slot selection. Specifically approxPlaceable and hasAnyItemThatWouldPlace would need to consider buckets the item that places the respective liquid block.



Then #436 is the place for you I guess.

commented

Yeah, The problem I am running into is the approxPlaceable() returns a list of IBlockState objects, so a water bucket will never be able to be recognized as part of the inventory.

I think I could create a separate approxPlaceable method that will return the ItemStack object straight from the players inventory. Then, if water is trying to be placed, the ItemStack list can be used to find the water bucket and where it should be placed. This would be done in the searchForPlaceables method and its submethods.

Another part of it is modifying the schematic to only place on every other block, as well as code to pick up the newly generated water, buts thats for another day.

Thank you for the help though, im new to Minecraft modding so trying to understand all the object types and how the builder process works is a bit tough.

commented

The reason why water buckets are not considered is here. water buckets are not block items and are skipped here when checking the player inventory for placeable blocks.

ItemStack stack = ctx.player().inventory.mainInventory.get(i);
if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) {
continue;
}

commented

Yeah, The problem I am running into is the approxPlaceable() returns a list of IBlockState objects, so a water bucket will never be able to be recognized as part of the inventory.

I think there's a misconception about the purpose of approxPlaceable(). It is there to determine (approximately, precise is hard to do) which blocks the player can place with the items in their inventory, so things like buildSubstitutes can change the schematic based on available materials. It is not there determine which stack to use to place a specific state (that's done in the function rycbar0 pointed out).

I haven't tried implementing this so far so there are likely some oversights, but the main changes I expect are

  1. In approxPlaceable, put the default liquid state into the list instead of air for buckets
  2. In hasAnyItemThatWouldPlace, if the item is not an ItemBlock but is an ItemBucket, then set wouldBePlaced to the default state of the contained liquid and the rest is the same as for ItemBlockss.

In case you don't know how to get the "default state of the contained liquid": ItemBucket has a field containedBlock which holds the liquid block it places and the default state of a block is what's returned by block.getDefaultState().

commented

Thank you for the help, I was finally able to get water placing to work by following your changes.

However, I thought that the baritone movement pathfinding would be able to handle moving through the water, but unfortunately it is unable to. I think reworking the pathfinding system would be too difficult for me to do, so I will be taking a break from this project for now.

If anybody would like to continue, here is the code for the updated builder process.
BuilderProcess.txt