[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:
Xiaro 2020-12-24 23:53:04 -05:00 committed by GitHub
parent 0a571361a9
commit 1a82b29873
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 210 additions and 165 deletions

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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"))
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}