mirror of https://github.com/kami-blue/client
[refactor] Rewrite and greatly improve Scaffold (#1760)
* Cleaned up old codes * Added connection to ExecuteEvent * Added getHitVec(), added side parameter to getNeighbour * Fixed SafeWalk disabling view bobbing * Rewrote Scaffold * Added spoof hotbar * Added max range option * Added tower mode * Fixed hotbar spoofing * Get tower mode actually work on other servers * Added sneak option
This commit is contained in:
parent
0a571361a9
commit
1a82b29873
|
@ -4,6 +4,7 @@ import me.zeroeightsix.kami.util.Wrapper
|
|||
import net.minecraft.client.entity.EntityPlayerSP
|
||||
import net.minecraft.client.multiplayer.PlayerControllerMP
|
||||
import net.minecraft.client.multiplayer.WorldClient
|
||||
import net.minecraft.client.network.NetHandlerPlayClient
|
||||
import org.kamiblue.command.ExecuteEvent
|
||||
import org.kamiblue.command.IExecuteEvent
|
||||
|
||||
|
@ -12,18 +13,21 @@ abstract class AbstractClientEvent {
|
|||
abstract val world: WorldClient?
|
||||
abstract val player: EntityPlayerSP?
|
||||
abstract val playerController: PlayerControllerMP?
|
||||
abstract val connection: NetHandlerPlayClient?
|
||||
}
|
||||
|
||||
open class ClientEvent : AbstractClientEvent() {
|
||||
final override val world: WorldClient? = mc.world
|
||||
final override val player: EntityPlayerSP? = mc.player
|
||||
final override val playerController: PlayerControllerMP? = mc.playerController
|
||||
final override val connection: NetHandlerPlayClient? = mc.connection
|
||||
}
|
||||
|
||||
open class SafeClientEvent(
|
||||
override val world: WorldClient,
|
||||
override val player: EntityPlayerSP,
|
||||
override val playerController: PlayerControllerMP
|
||||
override val playerController: PlayerControllerMP,
|
||||
override val connection: NetHandlerPlayClient
|
||||
) : AbstractClientEvent()
|
||||
|
||||
class ClientExecuteEvent(
|
||||
|
@ -34,13 +38,14 @@ class SafeExecuteEvent(
|
|||
world: WorldClient,
|
||||
player: EntityPlayerSP,
|
||||
playerController: PlayerControllerMP,
|
||||
connection: NetHandlerPlayClient,
|
||||
event: ClientExecuteEvent
|
||||
) : SafeClientEvent(world, player, playerController), IExecuteEvent by event
|
||||
) : SafeClientEvent(world, player, playerController, connection), IExecuteEvent by event
|
||||
|
||||
fun ClientEvent.toSafe() =
|
||||
if (world != null && player != null && playerController != null) SafeClientEvent(world, player, playerController)
|
||||
if (world != null && player != null && playerController != null && connection != null) SafeClientEvent(world, player, playerController, connection)
|
||||
else null
|
||||
|
||||
fun ClientExecuteEvent.toSafe() =
|
||||
if (world != null && player != null && playerController != null) SafeExecuteEvent(world, player, playerController, this)
|
||||
if (world != null && player != null && playerController != null && connection != null) SafeExecuteEvent(world, player, playerController, connection, this)
|
||||
else null
|
|
@ -13,12 +13,13 @@ import me.zeroeightsix.kami.mixin.extension.*
|
|||
import me.zeroeightsix.kami.module.Module
|
||||
import me.zeroeightsix.kami.util.TimerUtils
|
||||
import me.zeroeightsix.kami.util.Wrapper
|
||||
import org.kamiblue.event.listener.listener
|
||||
import me.zeroeightsix.kami.util.math.Vec2f
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.play.client.CPacketHeldItemChange
|
||||
import net.minecraft.network.play.client.CPacketPlayer
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent
|
||||
import org.kamiblue.event.listener.listener
|
||||
import java.util.*
|
||||
|
||||
object PlayerPacketManager : Manager {
|
||||
|
@ -49,16 +50,16 @@ object PlayerPacketManager : Manager {
|
|||
}
|
||||
}
|
||||
|
||||
listener<PacketEvent.Send> {
|
||||
if (it.packet is CPacketHeldItemChange) {
|
||||
if (spoofingHotbar && it.packet.slotId != serverSideHotbar) {
|
||||
if (hotbarResetTimer.tick(2L)) {
|
||||
spoofingHotbar = false
|
||||
} else {
|
||||
it.cancel()
|
||||
}
|
||||
listener<PacketEvent.Send>(-69420) {
|
||||
if (it.packet is CPacketHeldItemChange) {
|
||||
if (spoofingHotbar && it.packet.slotId != serverSideHotbar) {
|
||||
if (hotbarResetTimer.tick(2L)) {
|
||||
spoofingHotbar = false
|
||||
} else {
|
||||
it.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listener<PacketEvent.PostSend>(-6969) {
|
||||
|
@ -113,11 +114,14 @@ object PlayerPacketManager : Manager {
|
|||
packetList[caller] = packet
|
||||
}
|
||||
|
||||
fun getHoldingItemStack(): ItemStack =
|
||||
Wrapper.player?.inventory?.mainInventory?.get(serverSideHotbar) ?: ItemStack.EMPTY
|
||||
|
||||
fun spoofHotbar(slot: Int) {
|
||||
Wrapper.minecraft.connection?.let {
|
||||
if (serverSideHotbar != slot) {
|
||||
it.sendPacket(CPacketHeldItemChange(slot))
|
||||
serverSideHotbar = slot
|
||||
it.sendPacket(CPacketHeldItemChange(slot))
|
||||
spoofingHotbar = true
|
||||
}
|
||||
hotbarResetTimer.reset()
|
||||
|
|
|
@ -32,9 +32,11 @@ public class MixinEntity {
|
|||
entity.isAirBorne = true;
|
||||
}
|
||||
|
||||
@Redirect(method = "move", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isSneaking()Z"))
|
||||
@Redirect(method = "move", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isSneaking()Z", ordinal = 0))
|
||||
public boolean isSneaking(Entity entity) {
|
||||
return SafeWalk.INSTANCE.shouldSafewalk() || Scaffold.INSTANCE.isEnabled() || entity.isSneaking();
|
||||
return SafeWalk.INSTANCE.shouldSafewalk()
|
||||
|| (Scaffold.INSTANCE.isEnabled() && Scaffold.INSTANCE.getSafeWalk().getValue())
|
||||
|| entity.isSneaking();
|
||||
}
|
||||
|
||||
// Makes the camera guy instead of original player turn around when we move mouse
|
||||
|
|
|
@ -3,6 +3,9 @@ package me.zeroeightsix.kami.module
|
|||
import com.google.common.base.Converter
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import me.zeroeightsix.kami.event.KamiEventBus
|
||||
import me.zeroeightsix.kami.gui.kami.DisplayGuiScreen
|
||||
import me.zeroeightsix.kami.module.modules.client.ClickGUI
|
||||
|
@ -211,5 +214,6 @@ open class Module {
|
|||
|
||||
protected companion object {
|
||||
@JvmField val mc: Minecraft = Minecraft.getMinecraft()
|
||||
val moduleScope = CoroutineScope(Dispatchers.Default + CoroutineName("KAMI Blue Module"))
|
||||
}
|
||||
}
|
|
@ -1,168 +1,172 @@
|
|||
package me.zeroeightsix.kami.module.modules.player
|
||||
|
||||
import me.zeroeightsix.kami.event.events.SafeTickEvent
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zeroeightsix.kami.event.KamiEvent
|
||||
import me.zeroeightsix.kami.event.events.OnUpdateWalkingPlayerEvent
|
||||
import me.zeroeightsix.kami.event.events.PacketEvent
|
||||
import me.zeroeightsix.kami.event.events.PlayerTravelEvent
|
||||
import me.zeroeightsix.kami.manager.managers.PlayerPacketManager
|
||||
import me.zeroeightsix.kami.mixin.client.entity.MixinEntity
|
||||
import me.zeroeightsix.kami.module.Module
|
||||
import me.zeroeightsix.kami.setting.Settings
|
||||
import me.zeroeightsix.kami.util.BlockUtils
|
||||
import me.zeroeightsix.kami.util.EntityUtils
|
||||
import org.kamiblue.event.listener.listener
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockContainer
|
||||
import net.minecraft.block.BlockFalling
|
||||
import me.zeroeightsix.kami.util.*
|
||||
import me.zeroeightsix.kami.util.BlockUtils.placeBlock
|
||||
import me.zeroeightsix.kami.util.EntityUtils.prevPosVector
|
||||
import me.zeroeightsix.kami.util.math.RotationUtils
|
||||
import me.zeroeightsix.kami.util.math.Vec2f
|
||||
import me.zeroeightsix.kami.util.math.VectorUtils.toBlockPos
|
||||
import net.minecraft.item.ItemBlock
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.play.client.CPacketEntityAction
|
||||
import net.minecraft.network.play.server.SPacketPlayerPosLook
|
||||
import net.minecraft.util.EnumFacing
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraftforge.client.event.InputUpdateEvent
|
||||
import kotlin.math.round
|
||||
import net.minecraft.util.math.RayTraceResult
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import org.kamiblue.event.listener.listener
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* @see MixinEntity.isSneaking
|
||||
*
|
||||
* TODO: Rewrite this
|
||||
*/
|
||||
@Module.Info(
|
||||
name = "Scaffold",
|
||||
category = Module.Category.PLAYER,
|
||||
description = "Places blocks under you"
|
||||
name = "Scaffold",
|
||||
category = Module.Category.PLAYER,
|
||||
description = "Places blocks under you",
|
||||
modulePriority = 500
|
||||
)
|
||||
object Scaffold : Module() {
|
||||
private val placeBlocks = register(Settings.b("PlaceBlocks", true))
|
||||
private val tower = register(Settings.b("Tower", true))
|
||||
private val modeSetting = register(Settings.e<Mode>("Mode", Mode.NORMAL))
|
||||
private val randomDelay = register(Settings.booleanBuilder("RandomDelay").withValue(false).withVisibility { modeSetting.value == Mode.LEGIT })
|
||||
private val delayRange = register(Settings.integerBuilder("DelayRange").withValue(6).withRange(0, 10).withVisibility { modeSetting.value == Mode.LEGIT && randomDelay.value })
|
||||
private val ticks = register(Settings.integerBuilder("Ticks").withValue(2).withRange(0, 60).withStep(2).withVisibility { modeSetting.value == Mode.NORMAL })
|
||||
private val spoofHotbar = register(Settings.b("SpoofHotbar", true))
|
||||
val safeWalk = register(Settings.b("SafeWalk", true))
|
||||
private val sneak = register(Settings.b("Sneak", true))
|
||||
private val delay = register(Settings.integerBuilder("Delay").withValue(2).withRange(1, 10).withStep(1))
|
||||
private val maxRange = register(Settings.integerBuilder("MaxRange").withValue(1).withRange(0, 3).withStep(1))
|
||||
|
||||
private var shouldSlow = false
|
||||
private var towerStart = 0.0
|
||||
private var holding = false
|
||||
private var lastRotation = Vec2f.ZERO
|
||||
private var placeInfo: Pair<EnumFacing, BlockPos>? = null
|
||||
private var inactiveTicks = 69
|
||||
|
||||
private enum class Mode {
|
||||
NORMAL, LEGIT
|
||||
private val placeTimer = TimerUtils.TickTimer(TimerUtils.TimeUnit.TICKS)
|
||||
private val rubberBandTimer = TimerUtils.TickTimer(TimerUtils.TimeUnit.TICKS)
|
||||
|
||||
override fun isActive(): Boolean {
|
||||
return isEnabled && inactiveTicks <= 5
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
placeInfo = null
|
||||
inactiveTicks = 69
|
||||
}
|
||||
|
||||
init {
|
||||
listener<InputUpdateEvent> {
|
||||
if (modeSetting.value == Mode.LEGIT && shouldSlow) {
|
||||
if (randomDelay.value) {
|
||||
it.movementInput.moveStrafe *= 0.2f + randomInRange
|
||||
it.movementInput.moveForward *= 0.2f + randomInRange
|
||||
} else {
|
||||
it.movementInput.moveStrafe *= 0.2f
|
||||
it.movementInput.moveForward *= 0.2f
|
||||
}
|
||||
}
|
||||
listener<PacketEvent.Receive> {
|
||||
if (it.packet !is SPacketPlayerPosLook) return@listener
|
||||
rubberBandTimer.reset()
|
||||
}
|
||||
|
||||
listener<SafeTickEvent> {
|
||||
shouldSlow = false
|
||||
|
||||
val towering = mc.gameSettings.keyBindJump.isKeyDown && tower.value
|
||||
var vec3d = EntityUtils.getInterpolatedPos(mc.player, ticks.value.toFloat())
|
||||
|
||||
if (modeSetting.value == Mode.LEGIT) vec3d = EntityUtils.getInterpolatedPos(mc.player, 0f)
|
||||
|
||||
val blockPos = BlockPos(vec3d).down()
|
||||
val belowBlockPos = blockPos.down()
|
||||
val legitPos = BlockPos(EntityUtils.getInterpolatedPos(mc.player, 2f))
|
||||
|
||||
/* when legitBridge is enabled */
|
||||
/* check if block behind player is air or other replaceable block and if it is, make the player crouch */
|
||||
if (modeSetting.value == Mode.LEGIT && mc.world.getBlockState(legitPos.down()).material.isReplaceable && mc.player.onGround && !towering) {
|
||||
shouldSlow = true
|
||||
mc.player.movementInput.sneak = true
|
||||
mc.player.connection.sendPacket(CPacketEntityAction(mc.player, CPacketEntityAction.Action.START_SNEAKING))
|
||||
}
|
||||
|
||||
if (towering) {
|
||||
if (mc.player.posY <= blockPos.y + 1.0f) {
|
||||
return@listener
|
||||
}
|
||||
}
|
||||
|
||||
if (!mc.world.getBlockState(blockPos).material.isReplaceable) {
|
||||
return@listener
|
||||
}
|
||||
|
||||
val oldSlot = mc.player.inventory.currentItem
|
||||
setSlotToBlocks(belowBlockPos)
|
||||
|
||||
/* check if we don't have a block adjacent to the blockPos */
|
||||
val neighbor = BlockUtils.getNeighbour(blockPos, attempts = 1) ?: return@listener
|
||||
|
||||
/* place the block */
|
||||
if (placeBlocks.value) BlockUtils.placeBlock(neighbor.second, neighbor.first)
|
||||
|
||||
/* Reset the slot */
|
||||
if (!holding) mc.player.inventory.currentItem = oldSlot
|
||||
|
||||
if (towering) {
|
||||
val motion = 0.42 // jump motion
|
||||
if (mc.player.onGround) {
|
||||
towerStart = mc.player.posY
|
||||
mc.player.motionY = motion
|
||||
}
|
||||
if (mc.player.posY > towerStart + motion) {
|
||||
mc.player.setPosition(mc.player.posX, round(mc.player.posY), mc.player.posZ)
|
||||
mc.player.motionY = motion
|
||||
towerStart = mc.player.posY
|
||||
}
|
||||
} else {
|
||||
towerStart = 0.0
|
||||
}
|
||||
|
||||
if (shouldSlow) {
|
||||
mc.player.connection.sendPacket(CPacketEntityAction(mc.player, CPacketEntityAction.Action.STOP_SNEAKING))
|
||||
shouldSlow = false
|
||||
listener<PlayerTravelEvent> {
|
||||
if (mc.player == null || !tower.value || !mc.gameSettings.keyBindJump.isKeyDown || inactiveTicks > 5 || !isHoldingBlock) return@listener
|
||||
if (rubberBandTimer.tick(10, false)) {
|
||||
if (shouldTower) mc.player.motionY = 0.41999998688697815
|
||||
} else if (mc.player.fallDistance <= 2.0f) {
|
||||
mc.player.motionY = -0.169
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val randomInRange: Float
|
||||
get() = 0.11f + Math.random().toFloat() * (delayRange.value / 10.0f - 0.11f)
|
||||
private val isHoldingBlock: Boolean
|
||||
get() = PlayerPacketManager.getHoldingItemStack().item is ItemBlock
|
||||
|
||||
private fun setSlotToBlocks(belowBlockPos: BlockPos) {
|
||||
if (isBlock(mc.player.heldItemMainhand, belowBlockPos)) {
|
||||
holding = true
|
||||
return
|
||||
private val shouldTower: Boolean
|
||||
get() = !mc.player.onGround
|
||||
&& mc.player.posY - floor(mc.player.posY) <= 0.1
|
||||
init {
|
||||
listener<OnUpdateWalkingPlayerEvent> { event ->
|
||||
if (mc.world == null || mc.player == null || event.era != KamiEvent.Era.PRE) return@listener
|
||||
inactiveTicks++
|
||||
placeInfo = calcNextPos()?.let {
|
||||
BlockUtils.getNeighbour(it, 1, sides = arrayOf(EnumFacing.DOWN))
|
||||
?: BlockUtils.getNeighbour(it, 3, sides = EnumFacing.HORIZONTALS)
|
||||
}
|
||||
|
||||
placeInfo?.let {
|
||||
val hitVec = BlockUtils.getHitVec(it.second, it.first)
|
||||
lastRotation = Vec2f(RotationUtils.getRotationTo(hitVec, true))
|
||||
swapAndPlace(it.second, it.first)
|
||||
}
|
||||
|
||||
if (inactiveTicks > 5) {
|
||||
PlayerPacketManager.resetHotbar()
|
||||
} else if (isHoldingBlock) {
|
||||
val packet = PlayerPacketManager.PlayerPacket(rotating = true, rotation = lastRotation)
|
||||
PlayerPacketManager.addPacket(this, packet)
|
||||
}
|
||||
}
|
||||
holding = false
|
||||
}
|
||||
|
||||
/* search blocks in hotbar */
|
||||
var newSlot = -1
|
||||
private fun calcNextPos(): BlockPos? {
|
||||
val posVec = mc.player.positionVector
|
||||
val blockPos = posVec.toBlockPos()
|
||||
return checkPos(blockPos)
|
||||
?: run {
|
||||
val realMotion = posVec.subtract(mc.player.prevPosVector)
|
||||
val nextPos = blockPos.add(roundToRange(realMotion.x), 0, roundToRange(realMotion.z))
|
||||
checkPos(nextPos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPos(blockPos: BlockPos): BlockPos? {
|
||||
val center = Vec3d(blockPos.x + 0.5, blockPos.y.toDouble(), blockPos.z + 0.5)
|
||||
val rayTraceResult = mc.world.rayTraceBlocks(
|
||||
center,
|
||||
center.subtract(0.0, 0.5, 0.0),
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
return blockPos.down().takeIf { rayTraceResult?.typeOfHit != RayTraceResult.Type.BLOCK }
|
||||
}
|
||||
|
||||
private fun roundToRange(value: Double) =
|
||||
(value * 2.5 * maxRange.value).roundToInt().coerceAtMost(maxRange.value)
|
||||
|
||||
private fun swapAndPlace(pos: BlockPos, side: EnumFacing) {
|
||||
getBlockSlot()?.let { slot ->
|
||||
if (spoofHotbar.value) PlayerPacketManager.spoofHotbar(slot)
|
||||
else InventoryUtils.swapSlot(slot)
|
||||
|
||||
inactiveTicks = 0
|
||||
|
||||
if (placeTimer.tick(delay.value.toLong())) {
|
||||
val shouldSneak = sneak.value && !mc.player.isSneaking
|
||||
moduleScope.launch {
|
||||
if (shouldSneak) {
|
||||
mc.player?.let {
|
||||
it.connection.sendPacket(CPacketEntityAction(it, CPacketEntityAction.Action.START_SNEAKING))
|
||||
}
|
||||
}
|
||||
delay(5)
|
||||
onMainThreadSafe {
|
||||
placeBlock(pos, side)
|
||||
if (shouldSneak) {
|
||||
connection.sendPacket(CPacketEntityAction(player, CPacketEntityAction.Action.STOP_SNEAKING))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBlockSlot(): Int? {
|
||||
mc.playerController.updateController()
|
||||
for (i in 0..8) {
|
||||
/* filter out non-block items */
|
||||
val stack = mc.player.inventory.getStackInSlot(i)
|
||||
|
||||
if (isBlock(stack, belowBlockPos)) {
|
||||
newSlot = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* check if any blocks were found, and if they were then set the slot */
|
||||
if (newSlot != -1) {
|
||||
mc.player.inventory.currentItem = newSlot
|
||||
val itemStack = mc.player.inventory.mainInventory[i]
|
||||
if (itemStack.isEmpty || itemStack.item !is ItemBlock) continue
|
||||
return i
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isBlock(stack: ItemStack, belowBlockPos: BlockPos): Boolean {
|
||||
/* filter out non-block items */
|
||||
if (stack == ItemStack.EMPTY || stack.getItem() !is ItemBlock) return false
|
||||
|
||||
val block = (stack.getItem() as ItemBlock).block
|
||||
if (BlockUtils.blackList.contains(block) || block is BlockContainer) return false
|
||||
|
||||
/* filter out non-solid blocks */
|
||||
if (!Block.getBlockFromItem(stack.getItem()).defaultState.isFullBlock) return false
|
||||
|
||||
/* don't use falling blocks if it'd fall */
|
||||
if ((stack.getItem() as ItemBlock).block is BlockFalling) {
|
||||
if (mc.world.getBlockState(belowBlockPos).material.isReplaceable) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
package me.zeroeightsix.kami.util
|
||||
|
||||
import me.zeroeightsix.kami.command.SafeClientEvent
|
||||
import me.zeroeightsix.kami.manager.managers.PlayerPacketManager
|
||||
import me.zeroeightsix.kami.util.math.RotationUtils
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.init.Blocks
|
||||
import net.minecraft.item.ItemBlock
|
||||
import net.minecraft.network.play.client.CPacketPlayer
|
||||
import net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock
|
||||
import net.minecraft.util.EnumFacing
|
||||
import net.minecraft.util.EnumHand
|
||||
import net.minecraft.util.SoundCategory
|
||||
import net.minecraft.util.math.AxisAlignedBB
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.RayTraceResult
|
||||
|
@ -54,16 +58,6 @@ object BlockUtils {
|
|||
|
||||
private val mc = Minecraft.getMinecraft()
|
||||
|
||||
fun placeBlock(pos: BlockPos, facing: EnumFacing) {
|
||||
val hitVecOffset = getHitVecOffset(facing)
|
||||
val rotation = RotationUtils.getRotationTo(Vec3d(pos).add(hitVecOffset), true)
|
||||
val rotationPacket = CPacketPlayer.PositionRotation(mc.player.posX, mc.player.posY, mc.player.posZ, rotation.x.toFloat(), rotation.y.toFloat(), mc.player.onGround)
|
||||
val placePacket = CPacketPlayerTryUseItemOnBlock(pos, facing, EnumHand.MAIN_HAND, hitVecOffset.x.toFloat(), hitVecOffset.y.toFloat(), hitVecOffset.z.toFloat())
|
||||
mc.connection!!.sendPacket(rotationPacket)
|
||||
mc.connection!!.sendPacket(placePacket)
|
||||
mc.player.swingArm(EnumHand.MAIN_HAND)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun faceVectorPacketInstant(vec: Vec3d) {
|
||||
val rotation = RotationUtils.getRotationTo(vec, true)
|
||||
|
@ -84,8 +78,14 @@ object BlockUtils {
|
|||
return rayTraceTo(blockPos)?.sideHit ?: EnumFacing.UP
|
||||
}
|
||||
|
||||
fun getHitVec(pos: BlockPos, facing: EnumFacing): Vec3d {
|
||||
val vec = facing.directionVec
|
||||
return Vec3d(vec.x * 0.5 + 0.5 + pos.x, vec.y * 0.5 + 0.5 + pos.y, vec.z * 0.5 + 0.5 + pos.z)
|
||||
}
|
||||
|
||||
fun getHitVecOffset(facing: EnumFacing): Vec3d {
|
||||
return Vec3d(facing.directionVec).scale(0.5).add(0.5, 0.5, 0.5)
|
||||
val vec = facing.directionVec
|
||||
return Vec3d(vec.x * 0.5 + 0.5, vec.y * 0.5 + 0.5, vec.z * 0.5 + 0.5)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,8 +178,14 @@ object BlockUtils {
|
|||
return null
|
||||
}
|
||||
|
||||
fun getNeighbour(blockPos: BlockPos, attempts: Int = 3, range: Float = 4.25f, toIgnore: HashSet<BlockPos> = HashSet()): Pair<EnumFacing, BlockPos>? {
|
||||
for (side in EnumFacing.values()) {
|
||||
fun getNeighbour(
|
||||
blockPos: BlockPos,
|
||||
attempts: Int = 3,
|
||||
range: Float = 4.25f,
|
||||
sides: Array<EnumFacing> = EnumFacing.values(),
|
||||
toIgnore: HashSet<BlockPos> = HashSet()
|
||||
): Pair<EnumFacing, BlockPos>? {
|
||||
for (side in sides) {
|
||||
val pos = blockPos.offset(side)
|
||||
if (!toIgnore.add(pos)) continue
|
||||
if (mc.world.getBlockState(pos).material.isReplaceable) continue
|
||||
|
@ -188,10 +194,10 @@ object BlockUtils {
|
|||
}
|
||||
if (attempts > 1) {
|
||||
toIgnore.add(blockPos)
|
||||
for (side in EnumFacing.values()) {
|
||||
for (side in sides) {
|
||||
val pos = blockPos.offset(side)
|
||||
if (!isPlaceable(pos)) continue
|
||||
return getNeighbour(pos, attempts - 1, range, toIgnore) ?: continue
|
||||
return getNeighbour(pos, attempts - 1, range, sides, toIgnore) ?: continue
|
||||
}
|
||||
}
|
||||
return null
|
||||
|
@ -211,4 +217,24 @@ object BlockUtils {
|
|||
mc.player.swingArm(EnumHand.MAIN_HAND)
|
||||
Thread.sleep((10f / placeSpeed).toLong())
|
||||
}
|
||||
|
||||
/**
|
||||
* Placing block without desync
|
||||
*/
|
||||
fun SafeClientEvent.placeBlock(pos: BlockPos, side: EnumFacing) {
|
||||
if (!isPlaceable(pos.offset(side))) return
|
||||
|
||||
val hitVecOffset = getHitVecOffset(side)
|
||||
val placePacket = CPacketPlayerTryUseItemOnBlock(pos, side, EnumHand.MAIN_HAND, hitVecOffset.x.toFloat(), hitVecOffset.y.toFloat(), hitVecOffset.z.toFloat())
|
||||
connection.sendPacket(placePacket)
|
||||
player.swingArm(EnumHand.MAIN_HAND)
|
||||
|
||||
val itemStack = PlayerPacketManager.getHoldingItemStack()
|
||||
val block = (itemStack.item as? ItemBlock?)?.block ?: return
|
||||
val metaData = itemStack.metadata
|
||||
val blockState = block.getStateForPlacement(world, pos, side, hitVecOffset.x.toFloat(), hitVecOffset.y.toFloat(), hitVecOffset.z.toFloat(), metaData, player, EnumHand.MAIN_HAND)
|
||||
val soundType = blockState.block.getSoundType(blockState, world, pos, player)
|
||||
world.playSound(player, pos, soundType.placeSound, SoundCategory.BLOCKS, (soundType.getVolume() + 1.0f) / 2.0f, soundType.getPitch() * 0.8f)
|
||||
}
|
||||
|
||||
}
|
|
@ -148,6 +148,6 @@ object VectorUtils {
|
|||
}
|
||||
|
||||
fun BlockPos.toVec3d(): Vec3d {
|
||||
return Vec3d(this).add(0.5, 0.5, 0.5)
|
||||
return Vec3d(x + 0.5, y + 0.5, z + 0.5)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue