[refactor] Fix issues with and improve AutoObsidian (#1804)

Co-authored-by: natan <66911017+natan515@users.noreply.github.com>
Co-authored-by: Xiaro <62033805+Xiaro@users.noreply.github.com>
Co-authored-by: theredstoner <huddy987@yahoo.com>
Co-authored-by: theredstoner <73187300+theredstoner@users.noreply.github.com>
Co-authored-by: zincodrone <59712311+zincodrone@users.noreply.github.com>
Co-authored-by: natan <2222natan@gmail.com>
Co-authored-by: Dominika <sokolov.dominika@gmail.com>
This commit is contained in:
Avanatiker 2021-01-06 21:19:01 +01:00 committed by GitHub
parent e430b82ccb
commit e83516bb7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 537 additions and 281 deletions

View File

@ -1,180 +1,245 @@
package me.zeroeightsix.kami.module.modules.misc
import baritone.api.pathing.goals.Goal
import baritone.api.pathing.goals.GoalNear
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.zeroeightsix.kami.event.Phase
import me.zeroeightsix.kami.event.SafeClientEvent
import me.zeroeightsix.kami.mixin.extension.rightClickDelayTimer
import me.zeroeightsix.kami.event.events.OnUpdateWalkingPlayerEvent
import me.zeroeightsix.kami.event.events.RenderWorldEvent
import me.zeroeightsix.kami.manager.managers.PlayerPacketManager
import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.process.AutoObsidianProcess
import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.util.BaritoneUtils
import me.zeroeightsix.kami.util.EntityUtils
import me.zeroeightsix.kami.util.EntityUtils.flooredPosition
import me.zeroeightsix.kami.util.*
import me.zeroeightsix.kami.util.EntityUtils.getDroppedItem
import me.zeroeightsix.kami.util.InventoryUtils
import me.zeroeightsix.kami.util.WorldUtils.isPlaceableForChest
import me.zeroeightsix.kami.util.math.RotationUtils
import me.zeroeightsix.kami.util.text.MessageSendHelper.sendChatMessage
import me.zeroeightsix.kami.util.WorldUtils.placeBlock
import me.zeroeightsix.kami.util.color.ColorHolder
import me.zeroeightsix.kami.util.graphics.ESPRenderer
import me.zeroeightsix.kami.util.math.RotationUtils.getRotationTo
import me.zeroeightsix.kami.util.math.VectorUtils
import me.zeroeightsix.kami.util.math.VectorUtils.toVec3dCenter
import me.zeroeightsix.kami.util.text.MessageSendHelper
import me.zeroeightsix.kami.util.threads.defaultScope
import me.zeroeightsix.kami.util.threads.onMainThreadSafe
import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.block.BlockShulkerBox
import net.minecraft.block.state.IBlockState
import net.minecraft.client.audio.PositionedSoundRecord
import net.minecraft.client.gui.inventory.GuiShulkerBox
import net.minecraft.enchantment.EnchantmentHelper
import net.minecraft.init.Blocks
import net.minecraft.init.Enchantments
import net.minecraft.init.Items
import net.minecraft.init.SoundEvents
import net.minecraft.inventory.ClickType
import net.minecraft.network.play.client.CPacketEntityAction
import net.minecraft.network.play.client.CPacketPlayerDigging
import net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock
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.util.math.Vec3d
import net.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.commons.extension.ceilToInt
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import org.kamiblue.commons.interfaces.DisplayEnum
import org.kamiblue.event.listener.listener
@Module.Info(
name = "AutoObsidian",
category = Module.Category.MISC,
description = "Mines ender chest automatically to fill inventory with obsidian"
description = "Breaks down Ender Chests to restock obsidian"
)
object AutoObsidian : Module() {
private val searchShulker = setting("SearchShulker", false)
private val autoRefill = setting("AutoRefill", false)
private val threshold = setting("RefillThreshold", 8, 1..56, 1, { autoRefill.value })
private val targetStacks = setting("TargetStacks", 1, 1..20, 1)
private val delayTicks = setting("DelayTicks", 5, 0..10, 1)
private val fillMode by setting("FillMode", FillMode.TARGET_STACKS)
private val searchShulker by setting("SearchShulker", false)
private val leaveEmptyShulkers by setting("LeaveEmptyShulkers", true, { searchShulker })
private val autoRefill by setting("AutoRefill", false, { fillMode != FillMode.INFINITE })
private val threshold by setting("RefillThreshold", 8, 1..56, 1, { autoRefill && fillMode != FillMode.INFINITE })
private val targetStacks by setting("TargetStacks", 1, 1..20, 1, { fillMode == FillMode.TARGET_STACKS })
private val delayTicks by setting("DelayTicks", 5, 0..10, 1)
private val rotationMode by setting("RotationMode", RotationMode.SPOOF)
private val maxReach by setting("MaxReach", 4.5f, 2.0f..6.0f, 0.1f)
enum class State {
SEARCHING, PLACING, PRE_MINING, MINING, COLLECTING, DONE
private enum class FillMode(override val displayName: String, val message: String) : DisplayEnum {
TARGET_STACKS("Target Stacks", "Target stacks reached"),
FILL_INVENTORY("Fill Inventory", "Inventory filled"),
INFINITE("Infinite", "")
}
private enum class SearchingState {
PLACING, OPENING, PRE_MINING, MINING, COLLECTING, DONE
enum class State(override val displayName: String) : DisplayEnum {
SEARCHING("Searching"),
PLACING("Placing"),
PRE_MINING("Pre Mining"),
MINING("Mining"),
COLLECTING("Collecting"),
DONE("Done")
}
var pathing = false
var goal: BlockPos? = null
var state = State.SEARCHING
private enum class SearchingState(override val displayName: String) : DisplayEnum {
PLACING("Placing"),
OPENING("Opening"),
PRE_MINING("Pre Mining"),
MINING("Mining"),
COLLECTING("Collecting"),
DONE("Done")
}
@Suppress("UNUSED")
private enum class RotationMode(override val displayName: String) : DisplayEnum {
OFF("Off"),
SPOOF("Spoof"),
VIEW_LOCK("View Lock")
}
var goal: Goal? = null; private set
var state = State.SEARCHING; private set
private var searchingState = SearchingState.PLACING
private var active = false
private var searchingState = SearchingState.PLACING
private var playerPos = BlockPos(0, -1, 0)
private var placingPos = BlockPos(0, -1, 0)
private var shulkerBoxId = 0
private var tickCount = 0
private var openTime = 0L
private var lastHitVec: Vec3d? = null
private val tickTimer = TickTimer(TimeUnit.TICKS)
private val rotateTimer = TickTimer(TimeUnit.TICKS)
private val shulkerOpenTimer = TickTimer(TimeUnit.TICKS)
private val renderer = ESPRenderer().apply { aFilled = 33; aOutline = 233 }
override fun isActive(): Boolean {
return isEnabled && active
}
override fun onEnable() {
if (mc.player == null) return
state = State.SEARCHING
}
override fun onDisable() {
reset()
}
init {
safeListener<TickEvent.ClientTickEvent> {
if (it.phase != TickEvent.Phase.END) return@safeListener
/* Just a delay */
if (tickCount < delayTicks.value) {
tickCount++
return@safeListener
} else tickCount = 0
if (it.phase != TickEvent.Phase.END || !tickTimer.tick(delayTicks.toLong())) return@safeListener
updateState()
when (state) {
/* Searching states */
State.SEARCHING -> {
if (searchShulker.value) {
when (searchingState) {
SearchingState.PLACING -> placeShulker(placingPos)
SearchingState.OPENING -> openShulker(placingPos)
SearchingState.PRE_MINING -> mineBlock(placingPos, true)
SearchingState.MINING -> mineBlock(placingPos, false)
SearchingState.COLLECTING -> collectDroppedItem(shulkerBoxId)
SearchingState.DONE -> {
/* Positions need to be updated after moving while collecting dropped shulker box */
val currentPos = player.flooredPosition
playerPos = currentPos
setPlacingPos()
}
}
} else searchingState = SearchingState.DONE
searchingState()
}
State.PLACING -> {
placeEnderChest(placingPos)
}
State.PRE_MINING -> {
mineBlock(placingPos, true)
}
State.MINING -> {
mineBlock(placingPos, false)
}
State.COLLECTING -> {
collectDroppedItem(Blocks.OBSIDIAN.id)
}
/* Main states */
State.PLACING -> placeEnderChest(placingPos)
State.PRE_MINING -> mineBlock(placingPos, true)
State.MINING -> mineBlock(placingPos, false)
State.COLLECTING -> collectDroppedItem(49)
State.DONE -> {
if (!autoRefill.value) {
sendChatMessage("$chatName Reached target stacks, disabling.")
AutoObsidian.disable()
if (!autoRefill) {
MessageSendHelper.sendChatMessage("$chatName ${fillMode.message}, disabling.")
disable()
} else {
if (active) sendChatMessage("$chatName Reached target stacks, stopping.")
if (active) MessageSendHelper.sendChatMessage("$chatName ${fillMode.message}, stopping.")
reset()
}
}
}
}
}
override fun onDisable() {
BaritoneUtils.primary?.pathingControlManager?.mostRecentInControl()?.let {
if (it.isPresent && it.get() == AutoObsidianProcess) {
it.get().onLostControl()
listener<RenderWorldEvent> {
if (state != State.DONE) renderer.render(clear = false, cull = true)
}
safeListener<OnUpdateWalkingPlayerEvent> { event ->
if (event.phase != Phase.PRE || rotateTimer.tick(20L, false)) return@safeListener
val rotation = lastHitVec?.let { getRotationTo(it) } ?: return@safeListener
when (rotationMode) {
RotationMode.SPOOF -> {
val packet = PlayerPacketManager.PlayerPacket(rotating = true, rotation = rotation)
PlayerPacketManager.addPacket(this@AutoObsidian, packet)
}
RotationMode.VIEW_LOCK -> {
player.rotationYaw = rotation.x
player.rotationPitch = rotation.y
}
else -> {
// Rotation off
}
}
}
reset()
}
private fun SafeClientEvent.updateState() {
val currentPos = player.flooredPosition
if (state != State.DONE && placingPos.y == -1) {
playerPos = currentPos
setPlacingPos()
}
if (state != State.DONE) {
updatePlacingPos()
if (!active && state != State.DONE) {
active = true
BaritoneUtils.primary?.pathingControlManager?.registerProcess(AutoObsidianProcess)
}
if (!active) {
active = true
BaritoneUtils.primary?.pathingControlManager?.registerProcess(AutoObsidianProcess)
}
/* Tell baritone to get you back to position */
if (state != State.DONE && state != State.COLLECTING && searchingState != SearchingState.COLLECTING) {
if (currentPos.x != playerPos.x || currentPos.z != playerPos.z) {
pathing = true
goal = playerPos
return
} else {
pathing = false
if (state != State.COLLECTING && searchingState != SearchingState.COLLECTING) {
goal = if (player.getDistanceSqToCenter(placingPos) > 2.0) {
GoalNear(placingPos, 2)
} else {
null
}
}
}
/* Updates main state */
updateMainState()
updateSearchingState()
}
/* Updates searching state */
if (state == State.SEARCHING && searchingState != SearchingState.DONE) {
updateSearchingState()
} else if (state != State.SEARCHING) {
searchingState = SearchingState.PLACING
private fun SafeClientEvent.updatePlacingPos() {
val eyePos = player.getPositionEyes(1f)
if (isPositionValid(placingPos, world.getBlockState(placingPos), eyePos)) return
val posList = VectorUtils.getBlockPosInSphere(eyePos, maxReach)
.sortedBy { it.distanceSqToCenter(eyePos.x, eyePos.y, eyePos.z) }
.map { it to world.getBlockState(it) }
.toList()
val pair = posList.find { it.second.block == Blocks.ENDER_CHEST || it.second.block is BlockShulkerBox }
?: posList.find { isPositionValid(it.first, it.second, eyePos) }
if (pair != null) {
placingPos = pair.first
renderer.clear()
renderer.add(pair.first, ColorHolder(64, 255, 64))
} else {
MessageSendHelper.sendChatMessage("$chatName No valid position for placing shulker box / ender chest nearby, disabling.")
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
disable()
}
}
private fun SafeClientEvent.isPositionValid(pos: BlockPos, blockState: IBlockState, eyePos: Vec3d) =
!world.getBlockState(pos.down()).material.isReplaceable
&& (blockState.block.let { it == Blocks.ENDER_CHEST || it is BlockShulkerBox }
|| WorldUtils.isPlaceable(pos))
&& world.isAirBlock(pos.up())
&& world.rayTraceBlocks(eyePos, pos.toVec3dCenter())?.let { it.typeOfHit == RayTraceResult.Type.MISS } ?: true
private fun SafeClientEvent.updateMainState() {
val obbyCount = countObby()
val passCountCheck = checkObbyCount()
state = when {
state == State.DONE && autoRefill.value && InventoryUtils.countItemAll(49) <= threshold.value -> {
state == State.DONE && autoRefill && InventoryUtils.countItemAll(Blocks.OBSIDIAN.id) < threshold -> {
State.SEARCHING
}
state == State.COLLECTING && getDroppedItem(49, 8.0f) == null -> {
state == State.COLLECTING && (!canPickUpObby() || getDroppedItem(Blocks.OBSIDIAN.id, 8.0f) == null) -> {
State.DONE
}
state != State.DONE && world.isAirBlock(placingPos) && obbyCount >= targetStacks.value -> {
state != State.DONE && world.isAirBlock(placingPos) && !passCountCheck -> {
State.COLLECTING
}
state == State.MINING && world.isAirBlock(placingPos) -> {
@ -183,199 +248,321 @@ object AutoObsidian : Module() {
state == State.PLACING && !world.isAirBlock(placingPos) -> {
State.PRE_MINING
}
state == State.SEARCHING && searchingState == SearchingState.DONE && obbyCount < targetStacks.value -> {
state == State.SEARCHING && searchingState == SearchingState.DONE && passCountCheck -> {
State.PLACING
}
else -> state
else -> {
state
}
}
}
/**
* Check if we can pick up more obsidian:
* There must be at least one slot which is either empty, or contains a stack of obsidian less than 64
*/
private fun SafeClientEvent.canPickUpObby(): Boolean {
return fillMode == FillMode.INFINITE || player.inventory?.mainInventory?.any {
it.isEmpty || it.item.id == Blocks.OBSIDIAN.id && it.count < 64
} ?: false
}
/**
* @return `true` if can still place more ender chest
*/
private fun SafeClientEvent.checkObbyCount(): Boolean {
val inventory = InventoryUtils.countItemAll(Blocks.OBSIDIAN.id)
val dropped = EntityUtils.getDroppedItems(Blocks.OBSIDIAN.id, 8.0f).sumBy { it.item.count }
return when (fillMode) {
FillMode.TARGET_STACKS -> {
((inventory + dropped) / 8.0f).ceilToInt() / 8 <= targetStacks
}
FillMode.FILL_INVENTORY -> {
countEmptySlots() - dropped >= 8
}
FillMode.INFINITE -> {
true
}
}
}
private fun SafeClientEvent.countEmptySlots(): Int {
return player.inventory?.mainInventory?.sumBy {
when {
it.isEmpty -> 64
it.item.id == Blocks.OBSIDIAN.id -> 64 - it.count
else -> 0
}
} ?: 0
}
private fun SafeClientEvent.updateSearchingState() {
searchingState = when {
searchingState == SearchingState.PLACING && InventoryUtils.countItemAll(130) > 0 -> {
SearchingState.DONE
}
searchingState == SearchingState.COLLECTING && getDroppedItem(shulkerBoxId, 8.0f) == null -> {
SearchingState.DONE
}
searchingState == SearchingState.MINING && world.isAirBlock(placingPos) -> {
if (InventoryUtils.countItemAll(130) > 0) {
SearchingState.COLLECTING
} else { /* In case if the shulker wasn't placed due to server lag */
SearchingState.PLACING
if (state == State.SEARCHING) {
if (searchingState != SearchingState.DONE) {
searchingState = when {
searchingState == SearchingState.PLACING && InventoryUtils.countItemAll(Blocks.ENDER_CHEST.id) > 0 -> {
SearchingState.DONE
}
searchingState == SearchingState.COLLECTING && getDroppedItem(shulkerBoxId, 8.0f) == null -> {
SearchingState.DONE
}
searchingState == SearchingState.MINING && world.isAirBlock(placingPos) -> {
if (InventoryUtils.countItemAll(Blocks.ENDER_CHEST.id) > 0) {
SearchingState.COLLECTING
} else {
// In case if the shulker wasn't placed due to server lag
SearchingState.PLACING
}
}
searchingState == SearchingState.OPENING && (InventoryUtils.countItemAll(Blocks.ENDER_CHEST.id) > 0
|| InventoryUtils.getSlots(0, 35, 0) == null) -> {
SearchingState.PRE_MINING
}
searchingState == SearchingState.PLACING && !world.isAirBlock(placingPos) -> {
if (world.getBlockState(placingPos).block is BlockShulkerBox) {
SearchingState.OPENING
} else {
// In case if the shulker wasn't placed due to server lag
SearchingState.PRE_MINING
}
}
else -> {
searchingState
}
}
}
searchingState == SearchingState.OPENING && (InventoryUtils.countItemAll(130) >= 64 || InventoryUtils.getSlots(0, 35, 0) == null) -> {
SearchingState.PRE_MINING
}
searchingState == SearchingState.PLACING && !world.isAirBlock(placingPos) -> {
if (world.getBlockState(placingPos).block is BlockShulkerBox) {
SearchingState.OPENING
} else { /* In case if the shulker wasn't placed due to server lag */
SearchingState.PRE_MINING
}
}
else -> searchingState
}
}
private fun countObby(): Int {
val inventory = InventoryUtils.countItemAll(49)
val dropped = EntityUtils.getDroppedItems(49, 8.0f).sumBy { it.item.count }
return ((inventory + dropped) / 8.0f).ceilToInt() / 8
}
private fun setPlacingPos() {
if (getPlacingPos().y != -1) {
placingPos = getPlacingPos()
} else {
sendChatMessage("$chatName No valid position for placing shulker box / ender chest nearby, disabling.")
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
this.disable()
return
searchingState = SearchingState.PLACING
}
}
private fun getPlacingPos(): BlockPos {
val pos = playerPos
var facing = EnumFacing.NORTH
for (i in 1..4) {
val posOffset = pos.offset(facing)
val posOffsetDiagonal = posOffset.offset(facing.rotateY())
when {
isPlaceableForChest(posOffset) -> return posOffset
isPlaceableForChest(posOffset.up()) -> return posOffset.up()
isPlaceableForChest(posOffsetDiagonal) -> return posOffsetDiagonal
isPlaceableForChest(posOffsetDiagonal.up()) -> return posOffsetDiagonal.up()
else -> facing = facing.rotateY()
}
}
return BlockPos(0, -1, 0)
}
private fun SafeClientEvent.lookAtBlock(pos: BlockPos) {
val vec3d = Vec3d(pos).add(0.5, 0.0, 0.5)
val lookAt = RotationUtils.getRotationTo(vec3d)
player.rotationYaw = lookAt.x
player.rotationPitch = lookAt.y
}
/* Tasks */
private fun SafeClientEvent.placeShulker(pos: BlockPos) {
for (i in 219..234) {
if (InventoryUtils.getSlotsHotbar(i) == null) {
if (i != 234) continue else {
sendChatMessage("$chatName No shulker box was found in hotbar, disabling.")
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
disable()
return
private fun SafeClientEvent.searchingState() {
if (searchShulker) {
when (searchingState) {
SearchingState.PLACING -> {
placeShulker(placingPos)
}
SearchingState.OPENING -> {
openShulker(placingPos)
}
SearchingState.PRE_MINING -> {
mineBlock(placingPos, true)
}
SearchingState.MINING -> {
mineBlock(placingPos, false)
}
SearchingState.COLLECTING -> {
collectDroppedItem(shulkerBoxId)
}
SearchingState.DONE -> {
updatePlacingPos()
}
}
shulkerBoxId = i
InventoryUtils.swapSlotToItem(i)
break
} else {
searchingState = SearchingState.DONE
}
}
private fun SafeClientEvent.placeShulker(pos: BlockPos) {
if (InventoryUtils.getSlotsHotbar(shulkerBoxId) == null && InventoryUtils.getSlotsNoHotbar(shulkerBoxId) != null) {
InventoryUtils.moveToHotbar(shulkerBoxId, Items.DIAMOND_PICKAXE.id)
} else {
for (i in 219..234) {
if (InventoryUtils.getSlotsHotbar(i) == null) {
if (i == 234) {
MessageSendHelper.sendChatMessage("$chatName No shulker box was found in hotbar, disabling.")
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
disable()
}
continue
}
shulkerBoxId = i
InventoryUtils.swapSlotToItem(i)
break
}
}
if (world.getBlockState(pos).block !is BlockShulkerBox) {
lookAtBlock(pos)
player.isSneaking = true
playerController.processRightClickBlock(player, world, pos.down(), EnumFacing.UP, mc.objectMouseOver.hitVec, EnumHand.MAIN_HAND)
player.swingArm(EnumHand.MAIN_HAND)
mc.rightClickDelayTimer = 4
placeBlock(pos)
}
}
private fun SafeClientEvent.placeEnderChest(pos: BlockPos) {
if (InventoryUtils.getSlotsHotbar(130) == null && InventoryUtils.getSlotsNoHotbar(130) != null) {
InventoryUtils.moveToHotbar(130, 278)
/* Case where we need to move ender chests into the hotbar */
if (InventoryUtils.getSlotsHotbar(Blocks.ENDER_CHEST.id) == null && InventoryUtils.getSlotsNoHotbar(Blocks.ENDER_CHEST.id) != null) {
InventoryUtils.moveToHotbar(Blocks.ENDER_CHEST.id, Items.DIAMOND_PICKAXE.id)
return
} else if (InventoryUtils.getSlots(0, 35, 130) == null) {
if (searchShulker.value) {
} else if (InventoryUtils.getSlots(0, 35, Blocks.ENDER_CHEST.id) == null) {
/* Case where we are out of ender chests */
if (searchShulker) {
state = State.SEARCHING
} else {
sendChatMessage("$chatName No ender chest was found in inventory, disabling.")
MessageSendHelper.sendChatMessage("$chatName No ender chest was found in inventory, disabling.")
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
disable()
return
}
}
InventoryUtils.swapSlotToItem(130)
lookAtBlock(pos)
player.isSneaking = true
playerController.processRightClickBlock(player, world, pos.down(), mc.objectMouseOver.sideHit, mc.objectMouseOver.hitVec, EnumHand.MAIN_HAND)
player.swingArm(EnumHand.MAIN_HAND)
mc.rightClickDelayTimer = 4
/* Else, we already have ender chests in the hotbar */
InventoryUtils.swapSlotToItem(Blocks.ENDER_CHEST.id)
placeBlock(pos)
}
private fun SafeClientEvent.openShulker(pos: BlockPos) {
lookAtBlock(pos)
if (mc.currentScreen !is GuiShulkerBox) {
/* Added a delay here so it doesn't spam right click and get you kicked */
if (System.currentTimeMillis() >= openTime + 2000L) {
openTime = System.currentTimeMillis()
playerController.processRightClickBlock(player, world, pos, mc.objectMouseOver.sideHit, mc.objectMouseOver.hitVec, EnumHand.MAIN_HAND)
}
} else {
/* Extra delay here to wait for the item list to be loaded */
Executors.newSingleThreadScheduledExecutor().schedule({
val currentContainer = player.openContainer
var enderChestSlot = -1
for (i in 0..26) {
if (currentContainer.inventory[i].item == Blocks.ENDER_CHEST) {
enderChestSlot = i
}
}
if (enderChestSlot != -1) {
playerController.windowClick(currentContainer.windowId, enderChestSlot, 0, ClickType.QUICK_MOVE, player)
if (mc.currentScreen is GuiShulkerBox) {
val container = player.openContainer
val slot = container.inventory.subList(0, 27).indexOfFirst { it.item.id == Blocks.ENDER_CHEST.id }
if (slot != -1) {
InventoryUtils.inventoryClick(container.windowId, slot, 0, ClickType.QUICK_MOVE)
player.closeScreen()
} else if (shulkerOpenTimer.tick(100, false)) { // Wait for maximum of 5 seconds
if (leaveEmptyShulkers && container.inventory.subList(0, 27).indexOfFirst { it.item.id != Items.AIR.id } == -1) {
searchingState = SearchingState.PRE_MINING
player.closeScreen()
} else {
sendChatMessage("$chatName No ender chest was found in shulker, disabling.")
MessageSendHelper.sendChatMessage("$chatName No ender chest was found in shulker, disabling.")
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
disable()
}
}, delayTicks.value * 50L, TimeUnit.MILLISECONDS)
}
} else {
val side = EnumFacing.getDirectionFromEntityLiving(pos, player)
val hitVecOffset = WorldUtils.getHitVecOffset(side)
lastHitVec = WorldUtils.getHitVec(pos, side)
rotateTimer.reset()
if (shulkerOpenTimer.tick(50)) {
defaultScope.launch {
delay(10L)
onMainThreadSafe {
connection.sendPacket(CPacketPlayerTryUseItemOnBlock(pos, side, EnumHand.MAIN_HAND, hitVecOffset.x.toFloat(), hitVecOffset.y.toFloat(), hitVecOffset.z.toFloat()))
player.swingArm(EnumHand.MAIN_HAND)
}
}
}
}
}
private fun SafeClientEvent.mineBlock(pos: BlockPos, pre: Boolean) {
if (InventoryUtils.getSlotsHotbar(278) == null && InventoryUtils.getSlotsNoHotbar(278) != null) {
InventoryUtils.moveToHotbar(278, 130)
return
} else if (InventoryUtils.getSlots(0, 35, 278) == null) {
sendChatMessage("$chatName No pickaxe was found in inventory, disabling.")
private fun SafeClientEvent.placeBlock(pos: BlockPos) {
val pair = WorldUtils.getNeighbour(pos, 1, 6.5f)
?: run {
MessageSendHelper.sendChatMessage("$chatName Can't find neighbour block")
return
}
lastHitVec = WorldUtils.getHitVec(pair.second, pair.first)
rotateTimer.reset()
connection.sendPacket(CPacketEntityAction(player, CPacketEntityAction.Action.START_SNEAKING))
defaultScope.launch {
delay(10L)
onMainThreadSafe {
placeBlock(pair.second, pair.first)
}
delay(10L)
onMainThreadSafe {
connection.sendPacket(CPacketEntityAction(player, CPacketEntityAction.Action.STOP_SNEAKING))
}
}
}
/**
* Gets the first hotbar slot of a diamond pickaxe that does not have the silk touch enchantment.
* @return The position of the pickaxe. -1 if there is no match.
*/
private fun SafeClientEvent.getHotbarNonSilkTouchPick(): Int {
val slotsWithPickaxes = InventoryUtils.getSlotsHotbar(Items.DIAMOND_PICKAXE.id)
?: return -1
for (slot in slotsWithPickaxes) {
if (EnchantmentHelper.getEnchantmentLevel(Enchantments.SILK_TOUCH, player.inventory.getStackInSlot(slot)) == 0) {
return slot
}
}
return -1
}
/**
* Gets the first non-hotbar slot of a diamond pickaxe that does not have the silk touch enchantment.
* @return The position of the pickaxe. -1 if there is no match.
*/
private fun SafeClientEvent.getInventoryNonSilkTouchPick(): Int {
val slotsWithPickaxes = InventoryUtils.getSlotsNoHotbar(Items.DIAMOND_PICKAXE.id)
?: return -1
for (slot in slotsWithPickaxes) {
if (EnchantmentHelper.getEnchantmentLevel(Enchantments.SILK_TOUCH, player.inventory.getStackInSlot(slot)) == 0) {
return slot
}
}
return -1
}
/**
* Swaps the active hotbar slot to one which has a valid pickaxe (i.e. non-silk touch). If there is no valid pickaxe,
* disable the module.
*/
private fun SafeClientEvent.swapToValidPickaxe() {
var hotbarPickaxeSlot = getHotbarNonSilkTouchPick()
val inventoryPickaxeSlot = getInventoryNonSilkTouchPick()
if ((hotbarPickaxeSlot == -1) && (inventoryPickaxeSlot != -1)) {
/* Note that slot 36 in windowId 0 is the same as slot 0 in the hotbar */
InventoryUtils.moveToSlot(0, inventoryPickaxeSlot, 36)
hotbarPickaxeSlot = 0
} else if (hotbarPickaxeSlot == -1) {
MessageSendHelper.sendChatMessage("No valid pickaxe was found in inventory.")
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
disable()
return
}
InventoryUtils.swapSlotToItem(278)
lookAtBlock(pos)
/* Packet mining lol */
if (pre) {
connection.sendPacket(CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, pos, mc.objectMouseOver.sideHit))
if (state != State.SEARCHING) state = State.MINING else searchingState = SearchingState.MINING
} else {
connection.sendPacket(CPacketPlayerDigging(CPacketPlayerDigging.Action.STOP_DESTROY_BLOCK, pos, mc.objectMouseOver.sideHit))
InventoryUtils.swapSlot(hotbarPickaxeSlot)
}
private fun SafeClientEvent.mineBlock(pos: BlockPos, pre: Boolean) {
if (pre) swapToValidPickaxe()
val side = EnumFacing.getDirectionFromEntityLiving(pos, player)
lastHitVec = WorldUtils.getHitVec(pos, side)
rotateTimer.reset()
defaultScope.launch {
delay(5L)
onMainThreadSafe {
if (pre) {
connection.sendPacket(CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, pos, side))
if (state != State.SEARCHING) state = State.MINING else searchingState = SearchingState.MINING
} else {
connection.sendPacket(CPacketPlayerDigging(CPacketPlayerDigging.Action.STOP_DESTROY_BLOCK, pos, side))
}
player.swingArm(EnumHand.MAIN_HAND)
}
}
player.swingArm(EnumHand.MAIN_HAND)
}
private fun collectDroppedItem(itemId: Int) {
pathing = if (getDroppedItem(itemId, 16.0f) != null) {
goal = getDroppedItem(itemId, 16.0f)
true
} else false
goal = if (getDroppedItem(itemId, 8.0f) != null) {
GoalNear(getDroppedItem(itemId, 8.0f), 0)
} else {
null
}
}
private fun reset() {
active = false
pathing = false
goal = null
searchingState = SearchingState.PLACING
playerPos = BlockPos(0, -1, 0)
placingPos = BlockPos(0, -1, 0)
tickCount = 0
lastHitVec = null
}
/* End of tasks */
}
}

View File

@ -3,6 +3,7 @@ package me.zeroeightsix.kami.module.modules.misc
import me.zeroeightsix.kami.mixin.extension.syncCurrentPlayItem
import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.util.InventoryUtils
import me.zeroeightsix.kami.util.combat.CombatUtils
import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.block.state.IBlockState
@ -57,7 +58,7 @@ object AutoTool : Module() {
}
}
private fun equipBestTool(blockState: IBlockState) {
fun equipBestTool(blockState: IBlockState) {
var bestSlot = -1
var max = 0.0
@ -75,12 +76,7 @@ object AutoTool : Module() {
}
}
}
if (bestSlot != -1) equip(bestSlot)
}
private fun equip(slot: Int) {
mc.player.inventory.currentItem = slot
mc.playerController.syncCurrentPlayItem()
if (bestSlot != -1) InventoryUtils.swapSlot(bestSlot)
}
init {

View File

@ -2,9 +2,14 @@ package me.zeroeightsix.kami.module.modules.player
import me.zeroeightsix.kami.KamiMod
import me.zeroeightsix.kami.event.events.PacketEvent
import me.zeroeightsix.kami.mixin.extension.pitch
import me.zeroeightsix.kami.mixin.extension.yaw
import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.util.text.MessageSendHelper
import net.minecraft.network.play.client.CPacketPlayer
import net.minecraft.network.play.client.CPacketPlayerDigging
import net.minecraft.util.math.Vec3d
import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.event.listener.listener
@ -41,7 +46,16 @@ object PacketLogger : Module() {
return@listener
}
lines.add("${sdf.format(Date())}\n${it.packet.javaClass.simpleName}\n${it.packet.javaClass}\n${it.packet}\n\n")
lines.add("${sdf.format(Date())}\n${it.packet.javaClass.simpleName}\n${it.packet.javaClass}\n${it.packet}")
when (it.packet) {
is CPacketPlayerDigging -> {
lines.add("\nMining - ${it.packet.position}@${it.packet.facing} - ${it.packet.action}")
}
is CPacketPlayer -> {
lines.add("\nRotation - Pitch: ${it.packet.pitch} Yaw: ${it.packet.yaw}")
}
}
lines.add("\n\n")
}
safeListener<TickEvent.ClientTickEvent> {

View File

@ -1,15 +1,10 @@
package me.zeroeightsix.kami.process
import baritone.api.pathing.goals.GoalNear
import baritone.api.process.IBaritoneProcess
import baritone.api.process.PathingCommand
import baritone.api.process.PathingCommandType
import me.zeroeightsix.kami.module.modules.misc.AutoObsidian
/**
* Created by Xiaro on 13/07/20.
* Updated by Xiaro on 11/09/20
*/
object AutoObsidianProcess : IBaritoneProcess {
override fun isTemporary(): Boolean {
@ -17,22 +12,22 @@ object AutoObsidianProcess : IBaritoneProcess {
}
override fun priority(): Double {
return 2.0
return 3.0
}
override fun onLostControl() {}
override fun displayName0(): String {
return "AutoObsidian: " + AutoObsidian.state.toString().toLowerCase()
return "AutoObsidian: " + AutoObsidian.state.displayName
}
override fun isActive(): Boolean {
return AutoObsidian.isActive()
}
override fun onTick(p0: Boolean, p1: Boolean): PathingCommand? {
return if (AutoObsidian.pathing && AutoObsidian.goal != null) {
PathingCommand(GoalNear(AutoObsidian.goal, 0), PathingCommandType.SET_GOAL_AND_PATH)
} else PathingCommand(null, PathingCommandType.REQUEST_PAUSE)
override fun onTick(p0: Boolean, p1: Boolean): PathingCommand {
return AutoObsidian.goal?.let {
PathingCommand(it, PathingCommandType.SET_GOAL_AND_PATH)
} ?: PathingCommand(null, PathingCommandType.REQUEST_PAUSE)
}
}

View File

@ -12,7 +12,7 @@ object TemporaryPauseProcess : IBaritoneProcess {
}
override fun priority(): Double {
return 3.0
return 5.0
}
override fun isActive(): Boolean {

View File

@ -3,4 +3,6 @@ package me.zeroeightsix.kami.util
import net.minecraft.block.Block
import net.minecraft.item.Item
val Block.item: Item get() = Item.getItemFromBlock(this)
val Block.item: Item get() = Item.getItemFromBlock(this)
val Block.id: Int get() = Block.getIdFromBlock(this)

View File

@ -4,6 +4,7 @@ import kotlinx.coroutines.delay
import me.zeroeightsix.kami.event.SafeClientEvent
import me.zeroeightsix.kami.manager.managers.PlayerPacketManager
import me.zeroeightsix.kami.util.math.RotationUtils
import me.zeroeightsix.kami.util.math.corners
import me.zeroeightsix.kami.util.threads.runSafeSuspend
import net.minecraft.client.Minecraft
import net.minecraft.entity.Entity
@ -105,6 +106,14 @@ object WorldUtils {
return mc.world.getBlockState(pos).block == Blocks.WATER
}
/**
* Checks if given [pos] is able to place block in it
*
* @return true playing is not colliding with [pos] and there is block below it
*/
fun isPlaceable(pos: BlockPos, ignoreSelfCollide: Boolean = false) = mc.world.getBlockState(pos).material.isReplaceable
&& mc.world.checkNoEntityCollision(AxisAlignedBB(pos), if (ignoreSelfCollide) mc.player else null)
fun rayTraceTo(blockPos: BlockPos): RayTraceResult? {
return mc.world.rayTraceBlocks(mc.player.getPositionEyes(1f), Vec3d(blockPos).add(0.5, 0.5, 0.5))
}
@ -123,23 +132,21 @@ object WorldUtils {
return Vec3d(vec.x * 0.5 + 0.5, vec.y * 0.5 + 0.5, vec.z * 0.5 + 0.5)
}
/**
* Checks if given [pos] is able to place block in it
*
* @return true playing is not colliding with [pos] and there is block below it
*/
fun isPlaceable(pos: BlockPos, ignoreSelfCollide: Boolean = false) = mc.world.getBlockState(pos).material.isReplaceable
&& mc.world.checkNoEntityCollision(AxisAlignedBB(pos), if (ignoreSelfCollide) mc.player else null)
fun SafeClientEvent.rayTraceHitVec(pos: BlockPos) : RayTraceResult? {
val eyePos = player.getPositionEyes(1f)
val bb = world.getBlockState(pos).getSelectedBoundingBox(world, pos)
/**
* Checks if given [pos] is able to chest (air above) block in it
*
* @return true playing is not colliding with [pos] and there is block below it
*/
fun isPlaceableForChest(pos: BlockPos): Boolean {
return isPlaceable(pos) && !mc.world.getBlockState(pos.down()).material.isReplaceable && mc.world.isAirBlock(pos.up())
return world.rayTraceBlocks(eyePos, bb.center, false, false, true)?.takeIf {
it.isEqualTo(pos)
} ?: bb.corners(0.95).mapNotNull { corner ->
world.rayTraceBlocks(eyePos, corner, false, false, true)?.takeIf { it.isEqualTo(pos) }
}.minByOrNull {
it.hitVec?.distanceTo(eyePos) ?: 69420.0
}
}
private fun RayTraceResult.isEqualTo(pos: BlockPos) = typeOfHit == RayTraceResult.Type.BLOCK && blockPos == pos
suspend fun buildStructure(
placeSpeed: Float,
getPlaceInfo: (HashSet<BlockPos>) -> Pair<EnumFacing, BlockPos>?

View File

@ -0,0 +1,38 @@
package me.zeroeightsix.kami.util.math
import me.zeroeightsix.kami.util.math.VectorUtils.plus
import me.zeroeightsix.kami.util.math.VectorUtils.times
import me.zeroeightsix.kami.util.math.VectorUtils.toVec3d
import net.minecraft.util.EnumFacing
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.Vec3d
val AxisAlignedBB.xLength get() = maxX - minX
val AxisAlignedBB.yLength get() = maxY - minY
val AxisAlignedBB.zLength get() = maxY - minY
val AxisAlignedBB.lengths get() = Vec3d(xLength, yLength, zLength)
fun AxisAlignedBB.corners(scale: Double) : Array<Vec3d> {
val growSizes = lengths * (scale - 1.0)
return grow(growSizes.x, growSizes.y, growSizes.z).corners()
}
fun AxisAlignedBB.corners() = arrayOf(
Vec3d(minX, minY, minZ),
Vec3d(minX, minY, maxZ),
Vec3d(minX, maxY, minZ),
Vec3d(minX, maxY, maxZ),
Vec3d(maxX, minY, minZ),
Vec3d(maxX, minY, maxZ),
Vec3d(maxX, maxY, minZ),
Vec3d(maxX, maxY, maxZ),
)
fun AxisAlignedBB.side(side: EnumFacing, scale: Double = 0.5) : Vec3d {
val lengths = lengths
val sideDirectionVec = side.directionVec.toVec3d()
return lengths * sideDirectionVec * scale + center
}

View File

@ -13,16 +13,21 @@ import kotlin.math.roundToInt
enum class Direction(
override val displayName: String,
val displayNameXY: String,
val directionVec: Vec3i
val directionVec: Vec3i,
val isDiagonal: Boolean
) : DisplayEnum {
NORTH("North", "-Z", Vec3i(0, 0, -1)),
NORTH_EAST("North East", "+X -Z", Vec3i(1, 0, -1)),
EAST("East", "+X", Vec3i(1, 0, 0)),
SOUTH_EAST("South East", "+X +Z", Vec3i(1, 0, 1)),
SOUTH("South", "+Z", Vec3i(0, 0, 1)),
SOUTH_WEST("South West", "-X +Z", Vec3i(-1, 0, 1)),
WEST("West", "-X", Vec3i(-1, 0, 0)),
NORTH_WEST("North West", "-X -Z", Vec3i(-1, 0, -1));
NORTH("North", "-Z", Vec3i(0, 0, -1), false),
NORTH_EAST("North East", "+X -Z", Vec3i(1, 0, -1), true),
EAST("East", "+X", Vec3i(1, 0, 0), false),
SOUTH_EAST("South East", "+X +Z", Vec3i(1, 0, 1), true),
SOUTH("South", "+Z", Vec3i(0, 0, 1), false),
SOUTH_WEST("South West", "-X +Z", Vec3i(-1, 0, 1), true),
WEST("West", "-X", Vec3i(-1, 0, 0), false),
NORTH_WEST("North West", "-X -Z", Vec3i(-1, 0, -1), true);
fun clockwise(n: Int = 1) = values()[Math.floorMod((ordinal + n), 8)]
fun counterClockwise(n: Int = 1) = values()[Math.floorMod((ordinal - n), 8)]
companion object {

View File

@ -72,7 +72,7 @@ object VectorUtils {
}
fun Vec3i.toVec3d(offSet: Vec3d): Vec3d {
return Vec3d(x+ offSet.x, y + offSet.y, z + offSet.z)
return Vec3d(x + offSet.x, y + offSet.y, z + offSet.z)
}
fun Vec3i.toVec3d(xOffset: Double, yOffset: Double, zOffset: Double): Vec3d {
@ -122,4 +122,16 @@ object VectorUtils {
fun Entity.distanceTo(chunkPos: ChunkPos): Double {
return hypot(chunkPos.x * 16 + 8 - posX, chunkPos.z * 16 + 8 - posZ)
}
fun Vec3i.multiply(multiplier: Int): Vec3i {
return Vec3i(x * multiplier, y * multiplier, z * multiplier)
}
infix operator fun Vec3d.times(vec3d: Vec3d): Vec3d = Vec3d(x * vec3d.x, y * vec3d.y, z * vec3d.z)
infix operator fun Vec3d.times(multiplier: Double): Vec3d = Vec3d(x * multiplier, y * multiplier, z * multiplier)
infix operator fun Vec3d.plus(vec3d: Vec3d): Vec3d = add(vec3d)
infix operator fun Vec3d.minus(vec3d: Vec3d): Vec3d = subtract(vec3d)
}

View File

@ -35,7 +35,7 @@ fun runSafe(block: SafeClientEvent.() -> Unit) {
ClientEvent().toSafe()?.let { block(it) }
}
fun <R> runSafe(block: SafeClientEvent.() -> R): R? {
fun <R> runSafeR(block: SafeClientEvent.() -> R): R? {
return ClientEvent().toSafe()?.let { block(it) }
}