diff --git a/src/main/java/me/zeroeightsix/kami/command/ExecuteEvents.kt b/src/main/java/me/zeroeightsix/kami/command/ExecuteEvents.kt index e8fc2deea..f8e247c65 100644 --- a/src/main/java/me/zeroeightsix/kami/command/ExecuteEvents.kt +++ b/src/main/java/me/zeroeightsix/kami/command/ExecuteEvents.kt @@ -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 \ No newline at end of file diff --git a/src/main/java/me/zeroeightsix/kami/manager/managers/PlayerPacketManager.kt b/src/main/java/me/zeroeightsix/kami/manager/managers/PlayerPacketManager.kt index 048731261..0b54ff967 100644 --- a/src/main/java/me/zeroeightsix/kami/manager/managers/PlayerPacketManager.kt +++ b/src/main/java/me/zeroeightsix/kami/manager/managers/PlayerPacketManager.kt @@ -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 { - if (it.packet is CPacketHeldItemChange) { - if (spoofingHotbar && it.packet.slotId != serverSideHotbar) { - if (hotbarResetTimer.tick(2L)) { - spoofingHotbar = false - } else { - it.cancel() - } + listener(-69420) { + if (it.packet is CPacketHeldItemChange) { + if (spoofingHotbar && it.packet.slotId != serverSideHotbar) { + if (hotbarResetTimer.tick(2L)) { + spoofingHotbar = false + } else { + it.cancel() } } + } } listener(-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() diff --git a/src/main/java/me/zeroeightsix/kami/mixin/client/entity/MixinEntity.java b/src/main/java/me/zeroeightsix/kami/mixin/client/entity/MixinEntity.java index 2bf7bc786..f80a61658 100644 --- a/src/main/java/me/zeroeightsix/kami/mixin/client/entity/MixinEntity.java +++ b/src/main/java/me/zeroeightsix/kami/mixin/client/entity/MixinEntity.java @@ -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 diff --git a/src/main/java/me/zeroeightsix/kami/module/Module.kt b/src/main/java/me/zeroeightsix/kami/module/Module.kt index 9cbb37b1e..27c94049c 100644 --- a/src/main/java/me/zeroeightsix/kami/module/Module.kt +++ b/src/main/java/me/zeroeightsix/kami/module/Module.kt @@ -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")) } } \ No newline at end of file diff --git a/src/main/java/me/zeroeightsix/kami/module/modules/player/Scaffold.kt b/src/main/java/me/zeroeightsix/kami/module/modules/player/Scaffold.kt index d0f2e53b4..d3ede9596 100644 --- a/src/main/java/me/zeroeightsix/kami/module/modules/player/Scaffold.kt +++ b/src/main/java/me/zeroeightsix/kami/module/modules/player/Scaffold.kt @@ -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.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? = 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 { - 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 { + if (it.packet !is SPacketPlayerPosLook) return@listener + rubberBandTimer.reset() } - listener { - 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 { + 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 { 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 - } } \ No newline at end of file diff --git a/src/main/java/me/zeroeightsix/kami/util/BlockUtils.kt b/src/main/java/me/zeroeightsix/kami/util/BlockUtils.kt index 55da9742a..b9159667e 100644 --- a/src/main/java/me/zeroeightsix/kami/util/BlockUtils.kt +++ b/src/main/java/me/zeroeightsix/kami/util/BlockUtils.kt @@ -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 = HashSet()): Pair? { - for (side in EnumFacing.values()) { + fun getNeighbour( + blockPos: BlockPos, + attempts: Int = 3, + range: Float = 4.25f, + sides: Array = EnumFacing.values(), + toIgnore: HashSet = HashSet() + ): Pair? { + 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) + } + } \ No newline at end of file diff --git a/src/main/java/me/zeroeightsix/kami/util/math/VectorUtils.kt b/src/main/java/me/zeroeightsix/kami/util/math/VectorUtils.kt index ed3ab81ee..c30f9afcb 100644 --- a/src/main/java/me/zeroeightsix/kami/util/math/VectorUtils.kt +++ b/src/main/java/me/zeroeightsix/kami/util/math/VectorUtils.kt @@ -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) } }