diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java
index c2d1e3c0..0093c3ce 100644
--- a/src/api/java/baritone/api/Settings.java
+++ b/src/api/java/baritone/api/Settings.java
@@ -435,10 +435,6 @@ public class Settings {
/**
* {@code true}: can mine blocks when in inventory, chat, or tabbed away in ESC menu
- *
- * {@code false}: works on cosmic prisons
- *
- * LOL
*/
public Setting leftClickWorkaround = new Setting<>(true);
diff --git a/src/api/java/baritone/api/utils/IPlayerContext.java b/src/api/java/baritone/api/utils/IPlayerContext.java
index 83040ab5..3fdca1fa 100644
--- a/src/api/java/baritone/api/utils/IPlayerContext.java
+++ b/src/api/java/baritone/api/utils/IPlayerContext.java
@@ -42,7 +42,9 @@ public interface IPlayerContext {
IWorldData worldData();
- RayTraceResult objectMouseOver();
+ default RayTraceResult objectMouseOver() {
+ return playerController().objectMouseOver();
+ }
default BetterBlockPos playerFeet() {
// TODO find a better way to deal with soul sand!!!!!
diff --git a/src/api/java/baritone/api/utils/IPlayerController.java b/src/api/java/baritone/api/utils/IPlayerController.java
index 969431b1..2b3c7fc5 100644
--- a/src/api/java/baritone/api/utils/IPlayerController.java
+++ b/src/api/java/baritone/api/utils/IPlayerController.java
@@ -17,12 +17,12 @@
package baritone.api.utils;
-import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ClickType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.GameType;
/**
@@ -31,8 +31,6 @@ import net.minecraft.world.GameType;
*/
public interface IPlayerController {
- boolean clickBlock(BlockPos pos, EnumFacing side);
-
boolean onPlayerDamageBlock(BlockPos pos, EnumFacing side);
void resetBlockRemoving();
@@ -43,6 +41,8 @@ public interface IPlayerController {
GameType getGameType();
+ RayTraceResult objectMouseOver();
+
default double getBlockReachDistance() {
return this.getGameType().isCreative() ? 5.0F : 4.5F;
}
diff --git a/src/main/java/baritone/bot/BotPlayerContext.java b/src/main/java/baritone/bot/BotPlayerContext.java
index 87d90dc8..a2fcae03 100644
--- a/src/main/java/baritone/bot/BotPlayerContext.java
+++ b/src/main/java/baritone/bot/BotPlayerContext.java
@@ -64,10 +64,4 @@ public class BotPlayerContext implements IPlayerContext {
public IWorldData worldData() {
return bot.getBaritone().getWorldProvider().getCurrentWorld();
}
-
- @Override
- public RayTraceResult objectMouseOver() {
- // TODO-yeet lol fix this
- return Minecraft.getMinecraft().objectMouseOver;
- }
}
diff --git a/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java b/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java
index ffa408e7..ceb1362c 100644
--- a/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java
+++ b/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java
@@ -358,6 +358,7 @@ public class BotNetHandlerPlayClient extends NetHandlerPlayClient {
this.playerController = new BotPlayerController(this.user);
this.world = this.user.getManager().getWorldProvider().getWorld(packetIn.getDimension());
this.player = new EntityBot(this.user, (Minecraft) this.client, this.world, this, new StatisticsManager(), new RecipeBookClient());
+ this.user.onWorldLoad(this.world, this.player, this.playerController);
this.player.preparePlayerToSpawn();
this.world.spawnEntity(this.player);
this.player.setEntityId(packetIn.getPlayerId());
@@ -370,8 +371,6 @@ public class BotNetHandlerPlayClient extends NetHandlerPlayClient {
this.world.registerBot(packetIn.getPlayerId(), this.player);
- this.user.onWorldLoad(this.world, this.player, this.playerController);
-
Helper.HELPER.logDirect("Initialized Player and World");
}
@@ -479,6 +478,7 @@ public class BotNetHandlerPlayClient extends NetHandlerPlayClient {
EntityBot prev = this.player;
this.player = new EntityBot(this.user, (Minecraft) this.client, this.world, this, prev.getStatFileWriter(), prev.getRecipeBook());
+ this.user.onWorldLoad(this.world, this.player, this.playerController);
// noinspection ConstantConditions
this.player.getDataManager().setEntryValues(prev.getDataManager().getAll());
this.player.preparePlayerToSpawn();
@@ -487,8 +487,6 @@ public class BotNetHandlerPlayClient extends NetHandlerPlayClient {
this.player.dimension = packetIn.getDimensionID();
this.player.setServerBrand(prev.getServerBrand());
this.playerController.setGameType(packetIn.getGameType());
-
- this.user.onWorldLoad(this.world, this.player, this.playerController);
}
@Override
diff --git a/src/main/java/baritone/bot/spec/BotPlayerController.java b/src/main/java/baritone/bot/spec/BotPlayerController.java
index fff38f9c..7a598527 100644
--- a/src/main/java/baritone/bot/spec/BotPlayerController.java
+++ b/src/main/java/baritone/bot/spec/BotPlayerController.java
@@ -19,13 +19,26 @@ package baritone.bot.spec;
import baritone.api.utils.IPlayerController;
import baritone.bot.IBaritoneUser;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockCommandBlock;
+import net.minecraft.block.BlockStructure;
+import net.minecraft.block.material.Material;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
import net.minecraft.inventory.ClickType;
import net.minecraft.item.ItemStack;
+import net.minecraft.item.ItemSword;
import net.minecraft.network.play.client.CPacketClickWindow;
+import net.minecraft.network.play.client.CPacketHeldItemChange;
+import net.minecraft.network.play.client.CPacketPlayerDigging;
import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.GameType;
+import net.minecraft.world.World;
/**
* @author Brady
@@ -35,29 +48,81 @@ public class BotPlayerController implements IPlayerController {
private final IBaritoneUser user;
private GameType gameType;
+ private RayTraceResult objectMouseOver;
+
+ private BlockPos currentBlock;
+ private ItemStack currentHittingItem;
+ private boolean hittingBlock;
+ private float blockDamage;
+ private int blockHitDelay;
+ private int heldItemServer;
public BotPlayerController(IBaritoneUser user) {
this.user = user;
- }
-
- @Override
- public boolean clickBlock(BlockPos pos, EnumFacing side) {
- return false;
+ this.currentHittingItem = ItemStack.EMPTY;
}
@Override
public boolean onPlayerDamageBlock(BlockPos pos, EnumFacing side) {
- return false;
+ this.syncHeldItem();
+
+ EntityBot player = this.user.getEntity();
+ World world = player.world;
+
+ if (this.blockHitDelay > 0) {
+ this.blockHitDelay--;
+ return true;
+ }
+
+ if (this.gameType.isCreative() && world.getWorldBorder().contains(pos)) {
+ player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, pos, side));
+ handleCreativeBreak(pos, side);
+ this.blockHitDelay = 5;
+ return true;
+ }
+
+ if (!this.isHittingPosition(pos)) {
+ return this.clickBlock(pos, side);
+ }
+
+ IBlockState state = world.getBlockState(pos);
+
+ if (state.getMaterial() == Material.AIR) {
+ this.hittingBlock = false;
+ return false;
+ }
+
+ this.blockDamage += state.getPlayerRelativeBlockHardness(player, world, pos);
+
+ if (this.blockDamage >= 1.0F) {
+ this.hittingBlock = false;
+ this.blockDamage = 0.0F;
+ this.blockHitDelay = 5;
+
+ player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.STOP_DESTROY_BLOCK, pos, side));
+ this.handleBreak(pos);
+ }
+
+ world.sendBlockBreakProgress(player.getEntityId(), this.currentBlock, (int) (this.blockDamage * 10.0F) - 1);
+ return true;
}
@Override
public void resetBlockRemoving() {
-
+ if (this.hittingBlock) {
+ this.hittingBlock = false;
+ this.blockDamage = 0.0F;
+ this.user.getEntity().resetCooldown();
+ this.user.getEntity().connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK, this.currentBlock, EnumFacing.DOWN));
+ }
}
@Override
public ItemStack windowClick(int windowId, int slotId, int mouseButton, ClickType type, EntityPlayer player) {
- return null;
+ short transactionID = player.openContainer.getNextTransactionID(player.inventory);
+ ItemStack stack = player.openContainer.slotClick(slotId, mouseButton, type, player);
+ this.user.getEntity().connection.sendPacket(new CPacketClickWindow(windowId, slotId, mouseButton, type, stack, transactionID));
+ return stack;
}
@Override
@@ -70,4 +135,146 @@ public class BotPlayerController implements IPlayerController {
public GameType getGameType() {
return this.gameType;
}
+
+ @Override
+ public RayTraceResult objectMouseOver() {
+ Entity entity = this.user.getEntity();
+
+ if (entity != null) {
+ double blockReachDistance = getBlockReachDistance();
+ this.objectMouseOver = entity.rayTrace(blockReachDistance, 1.0F);
+
+ // TODO: Entity collision
+ // This doesn't matter atm because the bot worlds don't even contain entities
+ }
+
+ return this.objectMouseOver;
+ }
+
+ private boolean clickBlock(BlockPos pos, EnumFacing side) {
+ EntityBot player = this.user.getEntity();
+ World world = player.world;
+
+ if (!canBreak(player, pos)) {
+ return false;
+ }
+
+ if (this.gameType.isCreative()) {
+ player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, pos, side));
+ handleCreativeBreak(pos, side);
+ this.blockHitDelay = 5;
+ } else if (!this.hittingBlock || !this.isHittingPosition(pos)) {
+ if (this.hittingBlock) {
+ player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK, this.currentBlock, side));
+ }
+
+ IBlockState state = world.getBlockState(pos);
+ player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, pos, side));
+
+ if (state.getMaterial() != Material.AIR) {
+ if (this.blockDamage == 0.0F) {
+ state.getBlock().onBlockClicked(world, pos, player);
+ }
+ if (state.getPlayerRelativeBlockHardness(player, player.world, pos) >= 1.0F) {
+ this.handleBreak(pos);
+ }
+ } else {
+ this.hittingBlock = true;
+ this.currentBlock = pos;
+ this.blockDamage = 0.0F;
+ this.currentHittingItem = player.getHeldItemMainhand();
+ world.sendBlockBreakProgress(player.getEntityId(), this.currentBlock, (int) (this.blockDamage * 10.0F) - 1);
+ }
+ }
+
+ return true;
+ }
+
+ private void handleBreak(BlockPos pos) {
+ EntityBot player = this.user.getEntity();
+ World world = player.world;
+
+ if (this.gameType.isCreative() && !player.getHeldItemMainhand().isEmpty() && player.getHeldItemMainhand().getItem() instanceof ItemSword) {
+ return;
+ }
+
+ IBlockState state = world.getBlockState(pos);
+ Block block = state.getBlock();
+
+ if ((block instanceof BlockCommandBlock || block instanceof BlockStructure) && !player.canUseCommandBlock()) {
+ return;
+ }
+
+ if (state.getMaterial() == Material.AIR) {
+ return;
+ }
+
+ block.onBlockHarvested(world, pos, state, player);
+
+ if (world.setBlockState(pos, Blocks.AIR.getDefaultState(), 11)) {
+ block.onPlayerDestroy(world, pos, state);
+ }
+
+ this.currentBlock = new BlockPos(this.currentBlock.getX(), -1, this.currentBlock.getZ());
+
+ if (!this.gameType.isCreative()) {
+ ItemStack stack = player.getHeldItemMainhand();
+
+ if (!stack.isEmpty()) {
+ stack.onBlockDestroyed(world, state, pos, player);
+
+ if (stack.isEmpty()) {
+ player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
+ }
+ }
+ }
+ }
+
+ private void handleCreativeBreak(BlockPos pos, EnumFacing side) {
+ EntityPlayer player = this.user.getEntity();
+ if (!player.world.extinguishFire(player, pos, side)) {
+ this.handleBreak(pos);
+ }
+ }
+
+ private boolean canBreak(EntityPlayer player, BlockPos pos) {
+ if (!player.world.getWorldBorder().contains(pos)) {
+ return false;
+ }
+
+ if (this.gameType.hasLimitedInteractions()) {
+ if (this.gameType == GameType.SPECTATOR) {
+ return false;
+ }
+
+ if (!player.isAllowEdit()) {
+ ItemStack stack = player.getHeldItemMainhand();
+
+ return !stack.isEmpty() && stack.canDestroy(player.world.getBlockState(pos).getBlock());
+ }
+ }
+ return true;
+ }
+
+ private boolean isHittingPosition(BlockPos pos) {
+ ItemStack stack = this.user.getEntity().getHeldItemMainhand();
+ boolean itemUnchanged = this.currentHittingItem.isEmpty() && stack.isEmpty();
+
+ if (!this.currentHittingItem.isEmpty() && !stack.isEmpty()) {
+ itemUnchanged = stack.getItem() == this.currentHittingItem.getItem()
+ && ItemStack.areItemStackTagsEqual(stack, this.currentHittingItem)
+ && (stack.isItemStackDamageable() || stack.getMetadata() == this.currentHittingItem.getMetadata());
+ }
+
+ return pos.equals(this.currentBlock) && itemUnchanged;
+ }
+
+ private void syncHeldItem() {
+ int heldItemClient = this.user.getEntity().inventory.currentItem;
+
+ if (heldItemClient != this.heldItemServer) {
+ this.heldItemServer = heldItemClient;
+ this.user.getEntity().connection.sendPacket(new CPacketHeldItemChange(this.heldItemServer));
+ }
+ }
}
diff --git a/src/main/java/baritone/utils/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java
index 9f5b970d..36f31c6d 100644
--- a/src/main/java/baritone/utils/BlockBreakHelper.java
+++ b/src/main/java/baritone/utils/BlockBreakHelper.java
@@ -31,11 +31,6 @@ import net.minecraft.util.math.RayTraceResult;
*/
public final class BlockBreakHelper implements Helper {
- /**
- * The last block that we tried to break, if this value changes
- * between attempts, then we re-initialize the breaking process.
- */
- private BlockPos lastBlock;
private boolean didBreakLastTick;
private IPlayerContext playerContext;
@@ -45,13 +40,9 @@ public final class BlockBreakHelper implements Helper {
}
public void tryBreakBlock(BlockPos pos, EnumFacing side) {
- if (!pos.equals(lastBlock)) {
- playerContext.playerController().clickBlock(pos, side);
- }
if (playerContext.playerController().onPlayerDamageBlock(pos, side)) {
playerContext.player().swingArm(EnumHand.MAIN_HAND);
}
- lastBlock = pos;
}
public void stopBreakingBlock() {
@@ -59,7 +50,6 @@ public final class BlockBreakHelper implements Helper {
if (playerContext.player() != null) {
playerContext.playerController().resetBlockRemoving();
}
- lastBlock = null;
}
private boolean fakeBreak() {
diff --git a/src/main/java/baritone/utils/player/PrimaryPlayerController.java b/src/main/java/baritone/utils/player/PrimaryPlayerController.java
index fa4ced9d..b4ff09e7 100644
--- a/src/main/java/baritone/utils/player/PrimaryPlayerController.java
+++ b/src/main/java/baritone/utils/player/PrimaryPlayerController.java
@@ -24,6 +24,7 @@ import net.minecraft.inventory.ClickType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.GameType;
/**
@@ -34,11 +35,6 @@ public enum PrimaryPlayerController implements IPlayerController, Helper {
INSTANCE;
- @Override
- public boolean clickBlock(BlockPos pos, EnumFacing side) {
- return mc.playerController.clickBlock(pos, side);
- }
-
@Override
public boolean onPlayerDamageBlock(BlockPos pos, EnumFacing side) {
return mc.playerController.onPlayerDamageBlock(pos, side);
@@ -63,4 +59,9 @@ public enum PrimaryPlayerController implements IPlayerController, Helper {
public GameType getGameType() {
return mc.playerController.getCurrentGameType();
}
+
+ @Override
+ public RayTraceResult objectMouseOver() {
+ return mc.objectMouseOver;
+ }
}