From 500139ca3aec9904b467007509d638ca9b389f5d Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 10 Nov 2024 17:38:08 -0500 Subject: [PATCH 01/69] first test for crystal aura --- .../module/modules/combat/CrystalAura.kt | 109 +++++++++++++----- 1 file changed, 81 insertions(+), 28 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 67a7adc28..1a53fe3d2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -19,56 +19,109 @@ package com.lambda.module.modules.combat import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.RotationSettings +import com.lambda.config.groups.Targeting import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.concurrentListener +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.RotationManager.requestRotation +import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.task.tasks.BreakBlock +import com.lambda.util.combat.Explosion.explosionDamage +import com.lambda.util.math.VecUtils.dist +import com.lambda.util.world.blockSearch +import net.minecraft.block.Blocks +import net.minecraft.block.SideShapeType +import net.minecraft.entity.LivingEntity import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction object CrystalAura : Module( name = "CrystalAura", description = "Automatically attacks entities with crystals", defaultTags = setOf(ModuleTag.COMBAT), ) { - private val page by setting("Page", Page.General) + private val page by setting("Page", Page.Targeting) + + /* Targeting */ + private val targeting = Targeting.Combat(this) { page == Page.Targeting } /* Rotation */ - private val rotation = RotationSettings(this) { page == Page.Targeting } + private val rotate by setting("Rotate", true) { page == Page.Rotation } + private val rotation = RotationSettings(this) { page == Page.Rotation && rotate } /* Placing */ - private val swap by setting("Swap", Hand.MAIN_HAND, "Automatically swap to crystals") { page == Page.Placing } - private val multiPlace by setting("Multi Place", true, "Place multiple crystals") { page == Page.Placing } - private val placeDelay by setting("Place Delay", 0, 0..20, 1, "Delay between crystal placements", unit = "ticks", visibility = { page == Page.Placing }) - private val placeRange by setting("Place Range", 5.0, 0.1..7.0, 0.1, "Range to place crystals from the player eyes", visibility = { page == Page.Placing }) - private val placeRangeWalls by setting("Place Range Walls", 3.5, 0.1..7.0, 0.1, "Range to place crystals through walls", visibility = { page == Page.Placing }) - private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..20.0, 0.5, "Minimum health to place a crystal", visibility = { page == Page.Placing }) - private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, "Maximum self damage to place a crystal", visibility = { page == Page.Placing }) - private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to place a crystal", visibility = { page == Page.Placing }) + private val doPlace by setting("Do Place", true) { page == Page.Placing } + private val placing = InteractionSettings(this) { page == Page.Placing } + private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing } + + //private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing } + private val minSeparation by setting("Minimum Crystal Separation", 2, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing } + + + private val placeDelay by setting("Place Delay", 0, 0..20, 1, description = "Delay between crystal placements", unit = "ticks") { page == Page.Placing } + private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..20.0, 0.5, description = "Minimum health to place a crystal") { page == Page.Placing } + private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, description = "Maximum self damage to place a crystal") { page == Page.Placing } + private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to place a crystal") { page == Page.Placing } /* Exploding */ - private val explode by setting("Explode", true, "Explode crystals") { page == Page.Exploding } - private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, "Delay between crystal explosions", unit = "ticks", visibility = { page == Page.Exploding }) - private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, "Range to explode crystals", visibility = { page == Page.Exploding }) - private val explodeRangeWalls by setting("Explode Range Walls", 3.5, 0.1..7.0, 0.1, "Range to explode crystals through walls", visibility = { page == Page.Exploding }) - private val preventDeath by setting("Prevent Death", true, "Prevent death from crystal explosions", visibility = { page == Page.Exploding }) - private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to explode a crystal", visibility = { page == Page.Exploding }) - private val noWeakness by setting("No Weakness", true, "Switch to a weapon when you have a weakness effect", visibility = { page == Page.Exploding }) + private val doExplode by setting("Do Explode", true) { page == Page.Exploding } + private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, description = "Delay between crystal explosions", unit = "ticks") { page == Page.Exploding } + private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, description = "Range to explode crystals") { page == Page.Exploding } + private val preventDeath by setting("Prevent Death", true, description = "Prevent death from crystal explosions") { page == Page.Exploding } + private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to explode a crystal") { page == Page.Exploding } + private val noWeakness by setting("No Weakness", true, description = "Switch to a weapon when you have a weakness effect") { page == Page.Exploding } /* Rendering */ + private val rendering = Targeting.ESP(this) { page == Page.Rendering } + + val targetEntity: LivingEntity? get() = targeting.target() + val validPositions = mutableListOf() + init { + BreakBlock + requestRotation( + onUpdate = { + val blockpos = + validPositions.removeFirstOrNull() ?: return@requestRotation null + if (!rotate) return@requestRotation null - /* Interaction */ - private val interac = InteractionSettings(this) // Canadian interbank meme + lookAtBlock(blockpos, rotation, placing) + }, + ) - private enum class Page { - General, Targeting, Placing, Exploding, Rendering + listener { + targetEntity?.let { target -> + // TODO: If we could provide our own selector to [PlaceFinder] we could use it instead of this + // Or maybe not since we want a fast way of finding the best positions + validPositions.clear() // not good + validPositions.addAll( + blockSearch( + range = placing.reach.toInt(), + step = minSeparation, + pos = target.blockPos, // Search around the target and not the player + ) { pos, state -> + !state.isSideSolid(world, pos, Direction.UP, SideShapeType.FULL) && + (state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK)) && + // If the distance between the player and the place position for the + // target is greater than the explosion range, we cannot use said + // position as it is impossible to reach + player dist pos <= explodeRange + }.keys + .asSequence() + // https://minecraft.wiki/w/Explosion#Damage + .sortedByDescending { explosionDamage(it, target, 6.0) } + ) + } + } } - init { - concurrentListener {} - - /*listener { event -> - event.lookAtEntity(rotation, interac, getClosestEntity(player.eyePos, placeRange) ?: return@listener) - }*/ + private enum class Page { + Targeting, + Rotation, + Placing, + Exploding, + Rendering } } From ca63e6d31e33bd31226dcc530f8ea8fd925a014d Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 5 Jan 2025 16:26:54 -0500 Subject: [PATCH 02/69] crystal aura --- .../com/lambda/config/groups/Targeting.kt | 6 +- .../module/modules/combat/CrystalAura.kt | 155 ++++++++++++------ .../util/collections/LimitedDecayQueue.kt | 16 +- 3 files changed, 112 insertions(+), 65 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index f9efb26db..424822880 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -162,12 +162,8 @@ abstract class Targeting( * @return The best [LivingEntity] target, or `null` if no valid target is found. */ fun target(): LivingEntity? = runSafe { - val predicate = { entity: LivingEntity -> - validate(player, entity) - } - return@runSafe fastEntitySearch(targetingRange) { - predicate(it) + validate(player, it) }.minByOrNull { priority.factor(this, it) } diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 1a53fe3d2..614f5a0e0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -20,22 +20,31 @@ package com.lambda.module.modules.combat import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.RotationSettings import com.lambda.config.groups.Targeting -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.interaction.RotationManager.requestRotation +import com.lambda.context.SafeContext +import com.lambda.event.events.RenderEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.interaction.RotationManager.rotate import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.tasks.BreakBlock -import com.lambda.util.combat.Explosion.explosionDamage +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.collections.LimitedDecayQueue +import com.lambda.util.combat.CombatUtils.explosionDamage import com.lambda.util.math.VecUtils.dist +import com.lambda.util.math.transform import com.lambda.util.world.blockSearch +import com.lambda.util.world.fastEntitySearch import net.minecraft.block.Blocks -import net.minecraft.block.SideShapeType +import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity +import net.minecraft.entity.decoration.EndCrystalEntity import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box import net.minecraft.util.math.Direction +import java.awt.Color +import java.time.Instant object CrystalAura : Module( name = "CrystalAura", @@ -55,63 +64,91 @@ object CrystalAura : Module( private val doPlace by setting("Do Place", true) { page == Page.Placing } private val placing = InteractionSettings(this) { page == Page.Placing } private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing } - + private val placeMethod by setting("Place Sort", DamageSort.Deadly) //private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing } - private val minSeparation by setting("Minimum Crystal Separation", 2, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing } - - - private val placeDelay by setting("Place Delay", 0, 0..20, 1, description = "Delay between crystal placements", unit = "ticks") { page == Page.Placing } - private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..20.0, 0.5, description = "Minimum health to place a crystal") { page == Page.Placing } - private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, description = "Maximum self damage to place a crystal") { page == Page.Placing } + private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing } + private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, description = "Delay between crystal placements", unit = "ms") { page == Page.Placing } + private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..36.0, 0.5, description = "Minimum health to place a crystal") { page == Page.Placing } private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to place a crystal") { page == Page.Placing } + private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, description = "Maximum self damage to place a crystal") { page == Page.Placing } /* Exploding */ private val doExplode by setting("Do Explode", true) { page == Page.Exploding } private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, description = "Delay between crystal explosions", unit = "ticks") { page == Page.Exploding } private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, description = "Range to explode crystals") { page == Page.Exploding } - private val preventDeath by setting("Prevent Death", true, description = "Prevent death from crystal explosions") { page == Page.Exploding } + private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to explode a crystal") { page == Page.Exploding } - private val noWeakness by setting("No Weakness", true, description = "Switch to a weapon when you have a weakness effect") { page == Page.Exploding } /* Rendering */ - private val rendering = Targeting.ESP(this) { page == Page.Rendering } + private val renderCrystals by setting("Render Crystal", true) { page == Page.Rendering } + private val crystalColor by setting("Color Comparator", ColorComparator.Damage) { page == Page.Rendering && renderCrystals } + private val crystalAlpha by setting("Color Alpha", 128, 0..255) + + private val placedCrystal = LimitedDecayQueue(64, 1000L) + private val target: LivingEntity? get() = targeting.target() + + private fun SafeContext.worldCrystals(target: LivingEntity): List { + return fastEntitySearch(explodeRange, pos = target.blockPos) + .sortedByDescending { explodeMethod.sorted(this, target, it.blockPos) } + } + + private fun SafeContext.validPositions(target: LivingEntity): Sequence { + return blockSearch( + range = placing.reach.toInt(), + step = minSeparation, // Need to change this behavior + pos = player.blockPos, + ) { pos, _ -> canPlace(pos, target) } + .keys.asSequence() + .sortedByDescending { placeMethod.sorted(this, target, it.up()) } // The explosion source of the crystal is not at its base + } + + private fun SafeContext.canPlace(pos: BlockPos, target: LivingEntity): Boolean { + // Checks if the distance between the player and the target + // and the player and the place position is <= to the placing range + return player dist target <= placing.reach && + player dist pos <= placing.reach && + // Checks if the support block is either obsidian or bedrock + (pos.blockState(world).isOf(Blocks.OBSIDIAN) + || pos.blockState(world).isOf(Blocks.BEDROCK)) && + // Checks if the crystal can be placed on top of a block + // It checks if there's another entity in the air block + // Or if there's another crystal 2 blocks around the air block + world.isAir(pos.up()) && + fastEntitySearch(0.5, pos.up()).isEmpty() && + fastEntitySearch(1.5, pos.up()).isEmpty() && // Doesn't handle the edge case where there is a crystal floating + + player.health + player.absorptionAmount >= placeMinHealth && + explosionDamage(pos, target, 6.0) >= placeMinDamage && + explosionDamage(pos, player, 6.0) <= placeMaxSelfDamage && + // Checks if the last crystal was set more than [placeDelay] ms ago + placedCrystal.peek()?.second?.plusMillis(placeDelay)?.isBefore(Instant.now()) ?: true + //if (multiPlace) !placedCrystal.any { (crystalPos, _) -> pos.up() == crystalPos } else true + } - val targetEntity: LivingEntity? get() = targeting.target() - val validPositions = mutableListOf() init { - BreakBlock - requestRotation( - onUpdate = { - val blockpos = - validPositions.removeFirstOrNull() ?: return@requestRotation null - if (!rotate) return@requestRotation null - - lookAtBlock(blockpos, rotation, placing) - }, - ) - - listener { - targetEntity?.let { target -> - // TODO: If we could provide our own selector to [PlaceFinder] we could use it instead of this - // Or maybe not since we want a fast way of finding the best positions - validPositions.clear() // not good - validPositions.addAll( - blockSearch( - range = placing.reach.toInt(), - step = minSeparation, - pos = target.blockPos, // Search around the target and not the player - ) { pos, state -> - !state.isSideSolid(world, pos, Direction.UP, SideShapeType.FULL) && - (state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK)) && - // If the distance between the player and the place position for the - // target is greater than the explosion range, we cannot use said - // position as it is impossible to reach - player dist pos <= explodeRange - }.keys - .asSequence() - // https://minecraft.wiki/w/Explosion#Damage - .sortedByDescending { explosionDamage(it, target, 6.0) } + rotate( + priority = 100, + ) { + onUpdate { + if (!rotate) return@onUpdate null + + val blockpos = validPositions( + target ?: return@onUpdate null + ).firstOrNull() ?: return@onUpdate null + + // Taskflow interact block + // placedCrystal.add() + lookAtBlock(blockpos, rotation, placing, sides = setOf(Direction.UP)) + } + } + + listen { + worldCrystals(target ?: return@listen).forEach { crystal -> + it.renderer.build( + Box.of(crystal.pos, 1.0, 1.0, 1.0), + crystalColor.compared(this, target ?: return@forEach, crystal.blockPos), + crystalColor.compared(this, target ?: return@forEach, crystal.blockPos) ) } } @@ -124,4 +161,22 @@ object CrystalAura : Module( Exploding, Rendering } + + private enum class DamageSort(val sorted: SafeContext.(LivingEntity, BlockPos) -> Double) { + Deadly({ target, source -> explosionDamage(source, target, 6.0) }), + Balanced({ target, source -> explosionDamage(source, player, 6.0) - explosionDamage(source, target, 6.0) }), + Safe({ target, source -> explosionDamage(source, target, 6.0) - explosionDamage(source, player, 6.0) }); + } + + private enum class ColorComparator(val compared: SafeContext.(LivingEntity, BlockPos) -> Color) { + Distance({ target, dest -> + val red = transform(target dist dest, 0.0, explodeRange, 255.0, 0.0).toInt() + Color(red, 255-red, 0, crystalAlpha) + }), + + Damage({ target, dest -> + val red = transform(explosionDamage(dest, target, 6.0), 0.0, target.health.toDouble(), 0.0, 255.0).toInt() + Color(red, 255-red, 0, crystalAlpha) + }) + } } diff --git a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt index ad97dffe3..a023078c8 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt @@ -23,17 +23,13 @@ import java.util.concurrent.ConcurrentLinkedQueue class LimitedDecayQueue( private var sizeLimit: Int, private var interval: Long, -) { - private val queue: ConcurrentLinkedQueue> = ConcurrentLinkedQueue() - - val size: Int - get() = queue.size - +) : ConcurrentLinkedQueue>() { @Synchronized + @JvmName("jvmAdd") fun add(element: E): Boolean { cleanUp() - return if (queue.size < sizeLimit) { - queue.add(element to Instant.now()) + return if (size < sizeLimit) { + add(element to Instant.now()) true } else { false @@ -55,8 +51,8 @@ class LimitedDecayQueue( @Synchronized private fun cleanUp() { val now = Instant.now() - while (queue.isNotEmpty() && now.minusMillis(interval).isAfter(queue.peek().second)) { - queue.poll() + while (isNotEmpty() && now.minusMillis(interval).isAfter(peek().second)) { + poll() } } } From f4cee650ff0f360928ec2a4f9bd614d6454f5a82 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:06:26 -0500 Subject: [PATCH 03/69] better checks and visualization --- .../module/modules/combat/CrystalAura.kt | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 614f5a0e0..fe360fe43 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -43,6 +43,7 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d import java.awt.Color import java.time.Instant @@ -93,11 +94,7 @@ object CrystalAura : Module( } private fun SafeContext.validPositions(target: LivingEntity): Sequence { - return blockSearch( - range = placing.reach.toInt(), - step = minSeparation, // Need to change this behavior - pos = player.blockPos, - ) { pos, _ -> canPlace(pos, target) } + return blockSearch(range = placing.reach.toInt()) { pos, _ -> canPlace(pos, target) } .keys.asSequence() .sortedByDescending { placeMethod.sorted(this, target, it.up()) } // The explosion source of the crystal is not at its base } @@ -107,6 +104,8 @@ object CrystalAura : Module( // and the player and the place position is <= to the placing range return player dist target <= placing.reach && player dist pos <= placing.reach && + // Checks if the position is within the player hitbox + !player.boundingBox.intersects(Box.of(pos.up().toCenterPos(), 1.0, 2.0, 1.0)) && // Checks if the support block is either obsidian or bedrock (pos.blockState(world).isOf(Blocks.OBSIDIAN) || pos.blockState(world).isOf(Blocks.BEDROCK)) && @@ -125,6 +124,8 @@ object CrystalAura : Module( //if (multiPlace) !placedCrystal.any { (crystalPos, _) -> pos.up() == crystalPos } else true } + private val testRender = mutableListOf() + init { rotate( @@ -133,9 +134,13 @@ object CrystalAura : Module( onUpdate { if (!rotate) return@onUpdate null - val blockpos = validPositions( + val poss = validPositions( target ?: return@onUpdate null - ).firstOrNull() ?: return@onUpdate null + ) + + testRender.addAll(poss.take(3)) + + val blockpos = poss.firstOrNull() ?: return@onUpdate null // Taskflow interact block // placedCrystal.add() @@ -146,11 +151,23 @@ object CrystalAura : Module( listen { worldCrystals(target ?: return@listen).forEach { crystal -> it.renderer.build( - Box.of(crystal.pos, 1.0, 1.0, 1.0), - crystalColor.compared(this, target ?: return@forEach, crystal.blockPos), - crystalColor.compared(this, target ?: return@forEach, crystal.blockPos) + Box.of(crystal.blockPos.toCenterPos(), 1.0, 1.0, 1.0), + crystalColor.compared(this, target ?: return@forEach, crystal.pos), + crystalColor.compared(this, target ?: return@forEach, crystal.pos) + ) + } + + testRender.forEachIndexed { index, pos -> + val center = pos.toCenterPos() + val blurple = Color(85, 57, 204, 50) + it.renderer.build( + Box.of(center, 1.0, 1.0, 1.0), + blurple, + blurple ) } + + testRender.clear() } } @@ -162,13 +179,25 @@ object CrystalAura : Module( Rendering } + /** + * Damage sorter parameter + * + * deadly -> always prioritize enemy damage no matter what + * balanced -> sort by the highest ratio of enemy damage to self damage + * safe -> always prioritize the least amount of self damage + */ private enum class DamageSort(val sorted: SafeContext.(LivingEntity, BlockPos) -> Double) { Deadly({ target, source -> explosionDamage(source, target, 6.0) }), Balanced({ target, source -> explosionDamage(source, player, 6.0) - explosionDamage(source, target, 6.0) }), Safe({ target, source -> explosionDamage(source, target, 6.0) - explosionDamage(source, player, 6.0) }); } - private enum class ColorComparator(val compared: SafeContext.(LivingEntity, BlockPos) -> Color) { + /** + * Comparator for different render modes + * + * @param compared Lambda that takes in a living target and crystal position and then returns a color + */ + private enum class ColorComparator(val compared: SafeContext.(LivingEntity, Vec3d) -> Color) { Distance({ target, dest -> val red = transform(target dist dest, 0.0, explodeRange, 255.0, 0.0).toInt() Color(red, 255-red, 0, crystalAlpha) From 0b6eff30abef42a0ca8a2c9dcbee5c820d739bab Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Wed, 8 Jan 2025 18:21:07 -0500 Subject: [PATCH 04/69] Update CrystalAura.kt --- .../module/modules/combat/CrystalAura.kt | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index fe360fe43..16bdf1ade 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -32,6 +32,7 @@ import com.lambda.util.BlockUtils.blockState import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.combat.CombatUtils.explosionDamage import com.lambda.util.math.VecUtils.dist +import com.lambda.util.math.VecUtils.vec3d import com.lambda.util.math.transform import com.lambda.util.world.blockSearch import com.lambda.util.world.fastEntitySearch @@ -39,6 +40,7 @@ import net.minecraft.block.Blocks import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.EndCrystalEntity +import net.minecraft.item.EndCrystalItem import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box @@ -96,7 +98,7 @@ object CrystalAura : Module( private fun SafeContext.validPositions(target: LivingEntity): Sequence { return blockSearch(range = placing.reach.toInt()) { pos, _ -> canPlace(pos, target) } .keys.asSequence() - .sortedByDescending { placeMethod.sorted(this, target, it.up()) } // The explosion source of the crystal is not at its base + .sortedByDescending { placeMethod.sorted(this, target, it.up()) } } private fun SafeContext.canPlace(pos: BlockPos, target: LivingEntity): Boolean { @@ -105,7 +107,7 @@ object CrystalAura : Module( return player dist target <= placing.reach && player dist pos <= placing.reach && // Checks if the position is within the player hitbox - !player.boundingBox.intersects(Box.of(pos.up().toCenterPos(), 1.0, 2.0, 1.0)) && + !player.boundingBox.intersects(Box.of(pos.vec3d, 1.0, 1.0, 1.0)) && // Checks if the support block is either obsidian or bedrock (pos.blockState(world).isOf(Blocks.OBSIDIAN) || pos.blockState(world).isOf(Blocks.BEDROCK)) && @@ -114,11 +116,11 @@ object CrystalAura : Module( // Or if there's another crystal 2 blocks around the air block world.isAir(pos.up()) && fastEntitySearch(0.5, pos.up()).isEmpty() && - fastEntitySearch(1.5, pos.up()).isEmpty() && // Doesn't handle the edge case where there is a crystal floating + fastEntitySearch(2.0, pos.up()).isEmpty() && // Replace by intersection check bc blockpos is corner - player.health + player.absorptionAmount >= placeMinHealth && - explosionDamage(pos, target, 6.0) >= placeMinDamage && - explosionDamage(pos, player, 6.0) <= placeMaxSelfDamage && + // player.health + player.absorptionAmount >= placeMinHealth && + explosionDamage(pos.up(), target, 6.0) >= placeMinDamage && + explosionDamage(pos.up(), player, 6.0) <= placeMaxSelfDamage && // Checks if the last crystal was set more than [placeDelay] ms ago placedCrystal.peek()?.second?.plusMillis(placeDelay)?.isBefore(Instant.now()) ?: true //if (multiPlace) !placedCrystal.any { (crystalPos, _) -> pos.up() == crystalPos } else true @@ -128,9 +130,7 @@ object CrystalAura : Module( init { - rotate( - priority = 100, - ) { + rotate { onUpdate { if (!rotate) return@onUpdate null @@ -138,7 +138,7 @@ object CrystalAura : Module( target ?: return@onUpdate null ) - testRender.addAll(poss.take(3)) + testRender.addAll(poss) val blockpos = poss.firstOrNull() ?: return@onUpdate null @@ -204,7 +204,10 @@ object CrystalAura : Module( }), Damage({ target, dest -> - val red = transform(explosionDamage(dest, target, 6.0), 0.0, target.health.toDouble(), 0.0, 255.0).toInt() + val damage = explosionDamage(dest, target, 6.0) + .coerceIn(0.0, target.health.toDouble()) + + val red = transform(damage, 0.0, target.health.toDouble(), 0.0, 255.0).toInt() Color(red, 255-red, 0, crystalAlpha) }) } From 63f358fb79f40cc9d1a9354ee2aac1cc20cf2ba4 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:57:22 -0500 Subject: [PATCH 05/69] removed caura delay check --- .../kotlin/com/lambda/module/modules/combat/CrystalAura.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 16bdf1ade..033e82ae0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -120,9 +120,9 @@ object CrystalAura : Module( // player.health + player.absorptionAmount >= placeMinHealth && explosionDamage(pos.up(), target, 6.0) >= placeMinDamage && - explosionDamage(pos.up(), player, 6.0) <= placeMaxSelfDamage && + explosionDamage(pos.up(), player, 6.0) <= placeMaxSelfDamage // Checks if the last crystal was set more than [placeDelay] ms ago - placedCrystal.peek()?.second?.plusMillis(placeDelay)?.isBefore(Instant.now()) ?: true + // placedCrystal.lastOrNull()?.second?.plusMillis(placeDelay)?.isBefore(Instant.now()) ?: true //if (multiPlace) !placedCrystal.any { (crystalPos, _) -> pos.up() == crystalPos } else true } From e93d412489405aee4f6e7667d85730f516f03b29 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:57:40 -0500 Subject: [PATCH 06/69] inlined killaura target --- .../lambda/module/modules/combat/KillAura.kt | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index 95311b4ab..823960fb3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -35,8 +35,6 @@ import com.lambda.interaction.visibilty.VisibilityChecker.scanSurfaces import com.lambda.interaction.visibilty.VisibilityChecker.visibleSides import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.threading.runConcurrent -import com.lambda.threading.runSafe import com.lambda.util.math.MathUtils.random import com.lambda.util.math.VecUtils.distSq import com.lambda.util.math.VecUtils.plus @@ -45,7 +43,6 @@ import com.lambda.util.math.lerp import com.lambda.util.player.MovementUtils.moveDiff import com.lambda.util.player.prediction.buildPlayerPrediction import com.lambda.util.world.raycast.RayCastUtils.entityResult -import kotlinx.coroutines.delay import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity import net.minecraft.entity.attribute.EntityAttributeModifier @@ -89,7 +86,8 @@ object KillAura : Module( private val selfPredict by setting("Self Predict", 1.0, 0.0..2.0, 0.1) { page == Page.Aiming && rotate } private val targetPredict by setting("Target Predict", 0.0, 0.0..2.0, 0.1) { page == Page.Aiming && rotate } - var target: LivingEntity? = null; private set + val target: LivingEntity? + get() = targeting.target() private var shakeRandom = Vec3d.ZERO @@ -131,7 +129,6 @@ object KillAura : Module( } listen { - target = targeting.target() if (!timerSync) attackTicks++ target?.let { entity -> @@ -139,16 +136,6 @@ object KillAura : Module( } } - runConcurrent { - while (true) { - delay(50) // ToDo: tps sync - - runSafe { - if (timerSync && isEnabled) attackTicks++ - } - } - } - listen { event -> if (event.packet !is HandSwingC2SPacket && event.packet !is UpdateSelectedSlotC2SPacket && @@ -327,7 +314,6 @@ object KillAura : Module( } private fun reset(ctx: SafeContext) = ctx.apply { - target = null attackTicks = player.lastAttackedTicks rotation.speedMultiplier = 1.0 shakeRandom = Vec3d.ZERO From f7f8794133d6b8430d94df8d0a01445933f478f6 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:57:56 -0500 Subject: [PATCH 07/69] feat: tps clock --- .../main/kotlin/com/lambda/module/hud/TPS.kt | 51 +++++++++++++++ .../main/kotlin/com/lambda/util/TpsClock.kt | 62 +++++++++++++++++++ .../kotlin/com/lambda/util/math/MathUtils.kt | 2 + 3 files changed, 115 insertions(+) create mode 100644 common/src/main/kotlin/com/lambda/module/hud/TPS.kt create mode 100644 common/src/main/kotlin/com/lambda/util/TpsClock.kt diff --git a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt new file mode 100644 index 000000000..d2eec6f02 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.hud + +import com.lambda.module.HudModule +import com.lambda.module.tag.ModuleTag +import com.lambda.util.TpsClock.normalizedTickRate +import com.lambda.util.math.MathUtils.format + +object TPS : HudModule( + name = "TPS", + description = "Display the server's tick rate", + defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.NETWORK), +) { + private val format by setting("Tick format", TickFormat.Tick) + + private val text: String get() = "${format.string}: ${format.output().format(2)}" + + // TODO: Replace by LambdaAtlas height cache + + override val height: Double get() = 20.0 + override val width: Double get() = 50.0 + + init { + onRender { + font.build(text, position) + } + } + + private enum class TickFormat(val output: () -> Double, val string: String) { + Tick({ normalizedTickRate * 20 }, "TPS"), + Milliseconds({ normalizedTickRate * 50 }, "MSPS"), + Normalized({ normalizedTickRate }, "TPSN"), + Percentage({ normalizedTickRate * 100 }, "TPS%") + } +} diff --git a/common/src/main/kotlin/com/lambda/util/TpsClock.kt b/common/src/main/kotlin/com/lambda/util/TpsClock.kt new file mode 100644 index 000000000..5c87a6ccd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/TpsClock.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.util + +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.PacketEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.util.collections.LimitedDecayQueue +import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket + +object TpsClock { + private val tickHistory = LimitedDecayQueue(120, 5000) + private var lastUpdate = 0L + + /** + * Returns the average tick rate normalized between 0 and 1 + */ + val normalizedTickRate: Double + get() { + val average = tickHistory.average() + return if (average.isNaN()) 1.0 else average + } + + /** + * Returns the average tick rate normalized multiplied by 20 + */ + val tickRate: Double get() = normalizedTickRate * 20 + + init { + listen(priority = 10000) { + if (it.packet !is WorldTimeUpdateS2CPacket) return@listen + + + if (lastUpdate != 0L) { + val timeElapsed = (System.nanoTime() - lastUpdate) / 1E9 + tickHistory.add((1 / timeElapsed).coerceIn(0.0, 1.0)) + } + + lastUpdate = System.nanoTime() + } + + listen { + tickHistory.clear() + lastUpdate = 0 + } + } +} diff --git a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt index b397cf190..d7e08b034 100644 --- a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt @@ -82,5 +82,7 @@ object MathUtils { fun Int.nextPowerOf2() = 2f.pow(ceil(log2(toFloat()))).toInt() + fun Double.format(scale: Int) = "%.${scale}f".format(this) + inline val Int.sq: Int get() = this * this } From 7631fa4c1780a6e4309602802729b2c457b16e61 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 03:26:13 +0100 Subject: [PATCH 08/69] Rework ServerTPS --- .../main/kotlin/com/lambda/module/hud/TPS.kt | 24 +++++++++------ .../lambda/module/modules/movement/Jesus.kt | 4 +-- .../lambda/module/modules/movement/Speed.kt | 4 +-- .../main/kotlin/com/lambda/util/Nameable.kt | 6 ++-- .../lambda/util/{TpsClock.kt => ServerTPS.kt} | 29 ++++++------------- .../kotlin/com/lambda/util/extension/Enum.kt | 11 ++++--- 6 files changed, 36 insertions(+), 42 deletions(-) rename common/src/main/kotlin/com/lambda/util/{TpsClock.kt => ServerTPS.kt} (65%) diff --git a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt index d2eec6f02..856a96b2d 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt @@ -19,7 +19,8 @@ package com.lambda.module.hud import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag -import com.lambda.util.TpsClock.normalizedTickRate +import com.lambda.util.NamedEnum +import com.lambda.util.ServerTPS.averageMSPerTick import com.lambda.util.math.MathUtils.format object TPS : HudModule( @@ -27,11 +28,11 @@ object TPS : HudModule( description = "Display the server's tick rate", defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.NETWORK), ) { - private val format by setting("Tick format", TickFormat.Tick) + private val format by setting("Tick format", TickFormat.TPS) - private val text: String get() = "${format.string}: ${format.output().format(2)}" + private val text: String get() = "${format.displayName}: ${format.output().format(2)}${format.unit}" - // TODO: Replace by LambdaAtlas height cache + // TODO: Replace by LambdaAtlas height cache and actually build a proper text with highlighted parameters override val height: Double get() = 20.0 override val width: Double get() = 50.0 @@ -42,10 +43,15 @@ object TPS : HudModule( } } - private enum class TickFormat(val output: () -> Double, val string: String) { - Tick({ normalizedTickRate * 20 }, "TPS"), - Milliseconds({ normalizedTickRate * 50 }, "MSPS"), - Normalized({ normalizedTickRate }, "TPSN"), - Percentage({ normalizedTickRate * 100 }, "TPS%") + @Suppress("unused") + private enum class TickFormat( + val output: () -> Double, + override val displayName: String, + val unit: String = "" + ) : NamedEnum { + TPS({ 1000 / averageMSPerTick }, "TPS"), + MSPT({ averageMSPerTick }, "MSPT", " ms"), + Normalized({ 50 / averageMSPerTick }, "TPS"), + Percentage({ 5000 / averageMSPerTick }, "TPS", "%") } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Jesus.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Jesus.kt index 1d36a2b89..59d7af103 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Jesus.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Jesus.kt @@ -25,7 +25,7 @@ import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.Nameable +import com.lambda.util.NamedEnum import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.VecUtils.minus import com.lambda.util.player.MovementUtils.isInputting @@ -53,7 +53,7 @@ object Jesus : Module( private var goUp = true private var swimmingTicks = 0 - enum class Mode(override val displayName: String, val collision: Boolean) : Nameable.NamedEnum { + enum class Mode(override val displayName: String, val collision: Boolean) : NamedEnum { NCP("NCP", true), NCP_DOLPHIN("NCP Dolphin", false), NCP_NEW("NCP New", true) diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 9982d0575..e0bae6393 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -28,7 +28,7 @@ import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.rotation.RotationMode import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.Nameable +import com.lambda.util.NamedEnum import com.lambda.util.extension.contains import com.lambda.util.player.MovementUtils.addSpeed import com.lambda.util.player.MovementUtils.calcMoveYaw @@ -85,7 +85,7 @@ object Speed : Module( private var ncpSpeed = NCP_BASE_SPEED private var lastDistance = 0.0 - enum class Mode(override val displayName: String) : Nameable.NamedEnum { + enum class Mode(override val displayName: String) : NamedEnum { GRIM_STRAFE("Grim Strafe"), NCP_STRAFE("NCP Strafe"), } diff --git a/common/src/main/kotlin/com/lambda/util/Nameable.kt b/common/src/main/kotlin/com/lambda/util/Nameable.kt index 6f7d4f674..4734ccdc0 100644 --- a/common/src/main/kotlin/com/lambda/util/Nameable.kt +++ b/common/src/main/kotlin/com/lambda/util/Nameable.kt @@ -22,8 +22,8 @@ package com.lambda.util */ interface Nameable { val name: String +} - interface NamedEnum { - val displayName: String - } +interface NamedEnum { + val displayName: String } diff --git a/common/src/main/kotlin/com/lambda/util/TpsClock.kt b/common/src/main/kotlin/com/lambda/util/ServerTPS.kt similarity index 65% rename from common/src/main/kotlin/com/lambda/util/TpsClock.kt rename to common/src/main/kotlin/com/lambda/util/ServerTPS.kt index 5c87a6ccd..2dfd6a28f 100644 --- a/common/src/main/kotlin/com/lambda/util/TpsClock.kt +++ b/common/src/main/kotlin/com/lambda/util/ServerTPS.kt @@ -23,39 +23,28 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.util.collections.LimitedDecayQueue import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket -object TpsClock { - private val tickHistory = LimitedDecayQueue(120, 5000) +object ServerTPS { + // Server sends exactly one world time update every 20 server ticks (one per second). + private val updateHistory = LimitedDecayQueue(61, 60000) private var lastUpdate = 0L - /** - * Returns the average tick rate normalized between 0 and 1 - */ - val normalizedTickRate: Double - get() { - val average = tickHistory.average() - return if (average.isNaN()) 1.0 else average - } - - /** - * Returns the average tick rate normalized multiplied by 20 - */ - val tickRate: Double get() = normalizedTickRate * 20 + val averageMSPerTick: Double + get() = (if (updateHistory.isEmpty()) 1000.0 else updateHistory.average()) / 20 init { listen(priority = 10000) { if (it.packet !is WorldTimeUpdateS2CPacket) return@listen - + val currentTime = System.currentTimeMillis() if (lastUpdate != 0L) { - val timeElapsed = (System.nanoTime() - lastUpdate) / 1E9 - tickHistory.add((1 / timeElapsed).coerceIn(0.0, 1.0)) + updateHistory.add(currentTime - lastUpdate) } - lastUpdate = System.nanoTime() + lastUpdate = currentTime } listen { - tickHistory.clear() + updateHistory.clear() lastUpdate = 0 } } diff --git a/common/src/main/kotlin/com/lambda/util/extension/Enum.kt b/common/src/main/kotlin/com/lambda/util/extension/Enum.kt index b0fd3c90e..b3e3e32a5 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/Enum.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/Enum.kt @@ -17,12 +17,11 @@ package com.lambda.util.extension -import com.lambda.util.Nameable +import com.lambda.util.NamedEnum +import com.lambda.util.StringUtils.capitalize val Enum<*>.displayValue get() = - (this as? Nameable.NamedEnum)?.displayName ?: name.split('_').joinToString(" ") { low -> - low.lowercase().replaceFirstChar { - if (it.isLowerCase()) it.titlecase() else it.toString() - } - } + (this as? NamedEnum)?.displayName ?: name.split('_').joinToString(" ") { low -> + low.capitalize() + } \ No newline at end of file From 9329c6f49b15824bffba59b5958acaa357031ccf Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 05:55:32 +0100 Subject: [PATCH 09/69] Fix esp builder bounding box not shifted by position --- .../esp/builders/StaticESPBuilders.kt | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt index c6ea98193..ad227bd39 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt @@ -30,42 +30,43 @@ import net.minecraft.util.math.Box import net.minecraft.util.shape.VoxelShape import java.awt.Color -fun StaticESPRenderer.buildMesh( +fun StaticESPRenderer.ofShape( pos: BlockPos, - state: BlockState, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR ) = runSafe { - val shape = state.getOutlineShape(world, pos) - buildMesh(shape, filledColor, outlineColor, sides, outlineMode) + val shape = pos.blockState(world).getOutlineShape(world, pos) + ofShape(pos, shape, filledColor, outlineColor, sides, outlineMode) } -fun StaticESPRenderer.buildMesh( +fun StaticESPRenderer.ofShape( pos: BlockPos, + state: BlockState, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR ) = runSafe { - val shape = pos.blockState(world).getOutlineShape(world, pos) - buildMesh(shape, filledColor, outlineColor, sides, outlineMode) + val shape = state.getOutlineShape(world, pos) + ofShape(pos, shape, filledColor, outlineColor, sides, outlineMode) } -fun StaticESPRenderer.buildMesh( +fun StaticESPRenderer.ofShape( + pos: BlockPos, shape: VoxelShape, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR ) { - shape.boundingBoxes - .forEach { build(it,filledColor, outlineColor, sides, outlineMode) } + shape.boundingBoxes.forEach { + ofBox(it.offset(pos), filledColor, outlineColor, sides, outlineMode) + } } - -fun StaticESPRenderer.build( +fun StaticESPRenderer.ofBox( box: Box, filledColor: Color, outlineColor: Color, @@ -76,26 +77,26 @@ fun StaticESPRenderer.build( buildOutline(box, outlineColor, sides, outlineMode) } -fun StaticESPRenderer.buildFilledMesh( +fun StaticESPRenderer.buildFilledShape( pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL, ) = runSafe { val shape = state.getOutlineShape(world, pos) - buildFilledMesh(shape, color, sides) + buildFilledShape(shape, color, sides) } -fun StaticESPRenderer.buildFilledMesh( +fun StaticESPRenderer.buildFilledShape( pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL, ) = runSafe { val shape = pos.blockState(world).getOutlineShape(world, pos) - buildFilledMesh(shape, color, sides) + buildFilledShape(shape, color, sides) } -fun StaticESPRenderer.buildFilledMesh( +fun StaticESPRenderer.buildFilledShape( shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL, @@ -111,7 +112,7 @@ fun StaticESPRenderer.buildFilled( sides: Int = DirectionMask.ALL ) = buildFilled(box, color, color, sides) -fun StaticESPRenderer.buildOutlineMesh( +fun StaticESPRenderer.buildOutlineShape( pos: BlockPos, state: BlockState, color: Color, @@ -119,20 +120,20 @@ fun StaticESPRenderer.buildOutlineMesh( outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR ) = runSafe { val shape = state.getOutlineShape(world, pos) - buildOutlineMesh(shape, color, sides, outlineMode) + buildOutlineShape(shape, color, sides, outlineMode) } -fun StaticESPRenderer.buildOutlineMesh( +fun StaticESPRenderer.buildOutlineShape( pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR ) = runSafe { val shape = pos.blockState(world).getOutlineShape(world, pos) - buildOutlineMesh(shape, color, sides, outlineMode) + buildOutlineShape(shape, color, sides, outlineMode) } -fun StaticESPRenderer.buildOutlineMesh( +fun StaticESPRenderer.buildOutlineShape( shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL, From b03a3d9f792c85ef2bed58d083276815bf0fc794 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 05:55:51 +0100 Subject: [PATCH 10/69] Add RotationMode.NONE --- .../lambda/config/groups/RotationConfig.kt | 2 + .../lambda/config/groups/RotationSettings.kt | 46 ++++--------------- .../com/lambda/interaction/RotationManager.kt | 11 ++--- 3 files changed, 16 insertions(+), 43 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationConfig.kt index a0e4d4b81..4a65e3da4 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationConfig.kt @@ -42,6 +42,8 @@ interface RotationConfig { */ val resetTicks: Int + val rotate: Boolean get() = rotationMode != RotationMode.NONE + interface Instant : RotationConfig { override val turnSpeed get() = 360.0 override val keepTicks get() = 1 diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 8981c468c..0c1998847 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -26,56 +26,28 @@ class RotationSettings( c: Configurable, vis: () -> Boolean = { true }, ) : RotationConfig { - /** - * The rotation mode - */ - override var rotationMode by c.setting( - "Mode", - RotationMode.SYNC, - "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera", - vis - ) + override var rotationMode by c.setting("Mode", RotationMode.SYNC, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", vis) - /** - * How many ticks to keep the rotation before resetting - */ - override val keepTicks by c.setting("Keep Rotation", 3, 0..10, 1, "Ticks to keep rotation", " ticks", vis) + /** How many ticks to keep the rotation before resetting */ + override val keepTicks by c.setting("Keep Rotation", 3, 0..10, 1, "Ticks to keep rotation", " ticks") { rotate && vis() } - /** - * How many ticks to wait before resetting the rotation - */ - override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks", vis) + /** How many ticks to wait before resetting the rotation */ + override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks") { rotate && vis() } - /** - * Whether the rotation is instant - */ - var instant by c.setting("Instant Rotation", true, "Instantly rotate", vis) + /** Whether the rotation is instant */ + var instant by c.setting("Instant Rotation", true, "Instantly rotate") { rotate && vis() } /** * The mean (average/base) value used to calculate rotation speed. * This value represents the center of the distribution. */ - private var mean by c.setting( - "Mean", - 40.0, - 1.0..120.0, - 0.1, - "Average rotation speed", - unit = "°" - ) { vis() && !instant } + private var mean by c.setting("Mean", 40.0, 1.0..120.0, 0.1, "Average rotation speed", unit = "°") { rotate && vis() && !instant } /** * The standard deviation for the Gaussian distribution used to calculate rotation speed. * This value represents the spread of rotation speed. */ - private var spread by c.setting( - "Spread", - 10.0, - 0.0..60.0, - 0.1, - "Spread of rotation speeds", - unit = "°" - ) { vis() && !instant } + private var spread by c.setting("Spread", 10.0, 0.0..60.0, 0.1, "Spread of rotation speeds", unit = "°") { rotate && vis() && !instant } /** * We must always provide turn speed to the interpolator because the player's yaw might exceed the -180 to 180 range. diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index 3e27cedf0..0de883fb7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -69,16 +69,15 @@ object RotationManager : Loadable { var lastCtx: RotationContext? = null listen(priority, alwaysListen) { event -> - val rotationContext = builder.onUpdate?.invoke(this, event.context) - - rotationContext?.let { - event.context = it + builder.onUpdate?.invoke(this, event.context)?.let { context -> + if (!context.config.rotate) return@let + event.context = context + lastCtx = context } - - lastCtx = rotationContext } listen { event -> + if (!event.context.config.rotate) return@listen if (event.context == lastCtx) { builder.onReceive?.invoke(this, event.context) } From 4341b8486c11ad8faba37879618a61a4cf70fd2e Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 05:57:10 +0100 Subject: [PATCH 11/69] ESP builder rename --- .../graphics/renderer/esp/builders/DynamicESPBuilders.kt | 2 +- .../com/lambda/interaction/rotation/RotationMode.kt | 4 +++- .../kotlin/com/lambda/module/modules/debug/BlockTest.kt | 4 ++-- .../kotlin/com/lambda/module/modules/debug/RenderTest.kt | 6 +++--- .../com/lambda/module/modules/movement/BackTrack.kt | 4 ++-- .../kotlin/com/lambda/module/modules/movement/Blink.kt | 4 ++-- .../kotlin/com/lambda/module/modules/player/Scaffold.kt | 4 ++-- .../kotlin/com/lambda/module/modules/render/BlockESP.kt | 8 ++++---- .../kotlin/com/lambda/module/modules/render/StorageESP.kt | 8 ++++---- 9 files changed, 23 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt index 1966ea40e..749648149 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt @@ -25,7 +25,7 @@ import com.lambda.util.extension.max import com.lambda.util.extension.min import java.awt.Color -fun DynamicESPRenderer.build( +fun DynamicESPRenderer.ofBox( box: DynamicAABB, filledColor: Color, outlineColor: Color, diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt index 133f9b24a..e59d10083 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt @@ -21,9 +21,11 @@ package com.lambda.interaction.rotation * @property SILENT Spoofing server-side rotation. * @property SYNC Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). * @property LOCK Locks the camera client-side. + * @property NONE No rotation. */ enum class RotationMode { SILENT, SYNC, - LOCK + LOCK, + NONE } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt index 380aafca0..1b051f8fc 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt @@ -19,7 +19,7 @@ package com.lambda.module.modules.debug import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.world.blockSearch @@ -54,7 +54,7 @@ object BlockTest : Module( state.isOf(Blocks.DIAMOND_BLOCK) }.forEach { (pos, state) -> state.getOutlineShape(world, pos).boundingBoxes.forEach { box -> - it.renderer.build(box.offset(pos), filledColor, outlineColor) + it.renderer.ofBox(box.offset(pos), filledColor, outlineColor) } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index 7a4b168f3..dc52b3bea 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -20,7 +20,7 @@ package com.lambda.module.modules.debug import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.DynamicAABB.Companion.dynamicBox -import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.setAlpha @@ -48,12 +48,12 @@ object RenderTest : Module( listen { entitySearch(8.0) .forEach { entity -> - it.renderer.build(entity.dynamicBox, filledColor, outlineColor) + it.renderer.ofBox(entity.dynamicBox, filledColor, outlineColor) } } listen { - it.renderer.build(Box.of(player.pos, 0.3, 0.3, 0.3), filledColor, outlineColor) + it.renderer.ofBox(Box.of(player.pos, 0.3, 0.3, 0.3), filledColor, outlineColor) } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt index c28202998..17b5efaff 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt @@ -24,7 +24,7 @@ import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.DynamicAABB -import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.combat.KillAura @@ -110,7 +110,7 @@ object BackTrack : Module( val p = target.hurtTime / 10.0 val c = lerp(p, c1, c2) - it.renderer.build(box, c.multAlpha(0.3), c.multAlpha(0.8)) + it.renderer.ofBox(box, c.multAlpha(0.3), c.multAlpha(0.8)) } listen { event -> diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt index 1b0eed92b..c309b1441 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt @@ -22,7 +22,7 @@ import com.lambda.event.events.PacketEvent import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.DynamicAABB -import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.combat.KillAura @@ -69,7 +69,7 @@ object Blink : Module( listen { event -> val color = GuiSettings.primaryColor - event.renderer.build(box.update(lastBox), color.setAlpha(0.3), color) + event.renderer.ofBox(box.update(lastBox), color.setAlpha(0.3), color) } listen { event -> diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index 328fd996a..884f804e4 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -26,7 +26,7 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh -import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.interaction.RotationManager.currentRotation import com.lambda.interaction.RotationManager.rotate import com.lambda.interaction.blockplace.PlaceFinder.Companion.buildPlaceInfo @@ -332,7 +332,7 @@ object Scaffold : Module( val box = Box(info.placedPos) val alpha = transform(seconds, 0.0, 0.5, 1.0, 0.0).coerceIn(0.0, 1.0) - event.renderer.build( + event.renderer.ofBox( box, c.multAlpha(0.3 * alpha), c.multAlpha(alpha), diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 09a849f2e..bb2694b45 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -21,8 +21,8 @@ import com.lambda.Lambda.mc import com.lambda.graphics.renderer.esp.ChunkedESP.Companion.newChunkedESP import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh -import com.lambda.graphics.renderer.esp.builders.buildFilledMesh -import com.lambda.graphics.renderer.esp.builders.buildOutlineMesh +import com.lambda.graphics.renderer.esp.builders.buildFilledShape +import com.lambda.graphics.renderer.esp.builders.buildOutlineShape import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -93,8 +93,8 @@ object BlockESP : Module( val shape = outlineShape(state, pos) val blockColor = blockColor(state, pos) - if (drawFaces) buildFilledMesh(shape, if (useBlockColor) blockColor else faceColor, sides) - if (drawOutlines) buildOutlineMesh(shape, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) + if (drawFaces) buildFilledShape(shape, if (useBlockColor) blockColor else faceColor, sides) + if (drawOutlines) buildOutlineShape(shape, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) } private fun rebuildMesh(from: Any, to: Any): Unit = esp.rebuild() diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt index 5fac5a4de..c21df9c9e 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -23,9 +23,9 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh import com.lambda.graphics.renderer.esp.builders.buildFilled -import com.lambda.graphics.renderer.esp.builders.buildFilledMesh +import com.lambda.graphics.renderer.esp.builders.buildFilledShape import com.lambda.graphics.renderer.esp.builders.buildOutline -import com.lambda.graphics.renderer.esp.builders.buildOutlineMesh +import com.lambda.graphics.renderer.esp.builders.buildOutlineShape import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -152,8 +152,8 @@ object StorageESP : Module( } else getBlockEntityColor(block) ?: return@runSafe val shape = outlineShape(block.cachedState, pos) - if (drawFaces) buildFilledMesh(shape, color.setAlpha(alpha), sides) - if (drawOutlines) buildOutlineMesh(shape, color, sides, outlineMode) + if (drawFaces) buildFilledShape(shape, color.setAlpha(alpha), sides) + if (drawOutlines) buildOutlineShape(shape, color, sides, outlineMode) } private fun StaticESPRenderer.build( From d262d00703992eac16dc5547ddb49f03f5a26e67 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 05:57:27 +0100 Subject: [PATCH 12/69] Dynamic targeting range --- common/src/main/kotlin/com/lambda/config/groups/Targeting.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index 424822880..c6a7c2c35 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -131,8 +131,10 @@ abstract class Targeting( */ class Combat( owner: Configurable, + defaultRange: Double = 5.0, + maxRange: Double = 16.0, predicate: () -> Boolean = { true }, - ) : Targeting(owner, predicate, 5.0, 16.0) { + ) : Targeting(owner, predicate, defaultRange, maxRange) { /** * The field of view limit for targeting entities. Configurable between 5 and 180 degrees. From 2ed4623271de6d5ad705c65cc715a9b2ded705cd Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 05:57:46 +0100 Subject: [PATCH 13/69] Use disconnect to clean up server tps --- common/src/main/kotlin/com/lambda/util/ServerTPS.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/util/ServerTPS.kt b/common/src/main/kotlin/com/lambda/util/ServerTPS.kt index 2dfd6a28f..b29903082 100644 --- a/common/src/main/kotlin/com/lambda/util/ServerTPS.kt +++ b/common/src/main/kotlin/com/lambda/util/ServerTPS.kt @@ -43,7 +43,7 @@ object ServerTPS { lastUpdate = currentTime } - listen { + listen { updateHistory.clear() lastUpdate = 0 } From 89ac6949dd191b7dc900b48d02cd71beb4d74d2e Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 05:59:50 +0100 Subject: [PATCH 14/69] Crystal Aura rewrite - Collect damage data once and use that for comparison etc - Use actual game checks for valid placements - Added InteractionSettings - Added RotationSettings --- .../module/modules/combat/CrystalAura.kt | 188 +++++++++--------- 1 file changed, 89 insertions(+), 99 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 033e82ae0..1afc2c41b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -23,7 +23,7 @@ import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.graphics.renderer.esp.builders.ofShape import com.lambda.interaction.RotationManager.rotate import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.Module @@ -34,20 +34,14 @@ import com.lambda.util.combat.CombatUtils.explosionDamage import com.lambda.util.math.VecUtils.dist import com.lambda.util.math.VecUtils.vec3d import com.lambda.util.math.transform -import com.lambda.util.world.blockSearch -import com.lambda.util.world.fastEntitySearch +import net.minecraft.block.BlockState import net.minecraft.block.Blocks -import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity -import net.minecraft.entity.decoration.EndCrystalEntity -import net.minecraft.item.EndCrystalItem import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box -import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import java.awt.Color -import java.time.Instant object CrystalAura : Module( name = "CrystalAura", @@ -57,17 +51,13 @@ object CrystalAura : Module( private val page by setting("Page", Page.Targeting) /* Targeting */ - private val targeting = Targeting.Combat(this) { page == Page.Targeting } - - /* Rotation */ - private val rotate by setting("Rotate", true) { page == Page.Rotation } - private val rotation = RotationSettings(this) { page == Page.Rotation && rotate } + // ToDo: Targeting Range should be reach + crystal range (also based on min damage) + private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting } /* Placing */ private val doPlace by setting("Do Place", true) { page == Page.Placing } - private val placing = InteractionSettings(this) { page == Page.Placing } private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing } - private val placeMethod by setting("Place Sort", DamageSort.Deadly) + private val placeMethod by setting("Place Sort", DamageSort.Deadly) { page == Page.Placing } //private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing } private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing } private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, description = "Delay between crystal placements", unit = "ms") { page == Page.Placing } @@ -79,117 +69,109 @@ object CrystalAura : Module( private val doExplode by setting("Do Explode", true) { page == Page.Exploding } private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, description = "Delay between crystal explosions", unit = "ticks") { page == Page.Exploding } private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, description = "Range to explode crystals") { page == Page.Exploding } - private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) + private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) { page == Page.Exploding } private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to explode a crystal") { page == Page.Exploding } + /* Rotation */ + private val rotation = RotationSettings(this) { page == Page.Rotation } + + /* Interaction */ + private val interact = InteractionSettings(this) { page == Page.Interaction } + /* Rendering */ private val renderCrystals by setting("Render Crystal", true) { page == Page.Rendering } private val crystalColor by setting("Color Comparator", ColorComparator.Damage) { page == Page.Rendering && renderCrystals } - private val crystalAlpha by setting("Color Alpha", 128, 0..255) + private val crystalAlpha by setting("Color Alpha", 128, 0..255) { page == Page.Rendering && renderCrystals } - private val placedCrystal = LimitedDecayQueue(64, 1000L) + private val placements = LimitedDecayQueue(64, 1000L) private val target: LivingEntity? get() = targeting.target() - private fun SafeContext.worldCrystals(target: LivingEntity): List { - return fastEntitySearch(explodeRange, pos = target.blockPos) - .sortedByDescending { explodeMethod.sorted(this, target, it.blockPos) } - } - - private fun SafeContext.validPositions(target: LivingEntity): Sequence { - return blockSearch(range = placing.reach.toInt()) { pos, _ -> canPlace(pos, target) } - .keys.asSequence() - .sortedByDescending { placeMethod.sorted(this, target, it.up()) } - } - - private fun SafeContext.canPlace(pos: BlockPos, target: LivingEntity): Boolean { - // Checks if the distance between the player and the target - // and the player and the place position is <= to the placing range - return player dist target <= placing.reach && - player dist pos <= placing.reach && - // Checks if the position is within the player hitbox - !player.boundingBox.intersects(Box.of(pos.vec3d, 1.0, 1.0, 1.0)) && - // Checks if the support block is either obsidian or bedrock - (pos.blockState(world).isOf(Blocks.OBSIDIAN) - || pos.blockState(world).isOf(Blocks.BEDROCK)) && - // Checks if the crystal can be placed on top of a block - // It checks if there's another entity in the air block - // Or if there's another crystal 2 blocks around the air block - world.isAir(pos.up()) && - fastEntitySearch(0.5, pos.up()).isEmpty() && - fastEntitySearch(2.0, pos.up()).isEmpty() && // Replace by intersection check bc blockpos is corner - - // player.health + player.absorptionAmount >= placeMinHealth && - explosionDamage(pos.up(), target, 6.0) >= placeMinDamage && - explosionDamage(pos.up(), player, 6.0) <= placeMaxSelfDamage - // Checks if the last crystal was set more than [placeDelay] ms ago - // placedCrystal.lastOrNull()?.second?.plusMillis(placeDelay)?.isBefore(Instant.now()) ?: true - //if (multiPlace) !placedCrystal.any { (crystalPos, _) -> pos.up() == crystalPos } else true - } - - private val testRender = mutableListOf() - - init { rotate { - onUpdate { - if (!rotate) return@onUpdate null - - val poss = validPositions( - target ?: return@onUpdate null - ) - - testRender.addAll(poss) + onUpdate { + val targetEntity = target ?: return@onUpdate null + val validPositions = findTargetPositions(targetEntity) - val blockpos = poss.firstOrNull() ?: return@onUpdate null + testRender.addAll(validPositions.map { it.blockPos }) - // Taskflow interact block - // placedCrystal.add() - lookAtBlock(blockpos, rotation, placing, sides = setOf(Direction.UP)) - } + validPositions.firstNotNullOfOrNull { + lookAtBlock(it.blockPos, rotation, interact) + } + } } listen { - worldCrystals(target ?: return@listen).forEach { crystal -> - it.renderer.build( - Box.of(crystal.blockPos.toCenterPos(), 1.0, 1.0, 1.0), - crystalColor.compared(this, target ?: return@forEach, crystal.pos), - crystalColor.compared(this, target ?: return@forEach, crystal.pos) - ) - } - - testRender.forEachIndexed { index, pos -> - val center = pos.toCenterPos() - val blurple = Color(85, 57, 204, 50) - it.renderer.build( - Box.of(center, 1.0, 1.0, 1.0), - blurple, - blurple - ) +// worldCrystals(target ?: return@listen).forEach { crystal -> +// it.renderer.build( +// Box.of(crystal.blockPos.toCenterPos(), 1.0, 1.0, 1.0), +// crystalColor.compared(this, target ?: return@forEach, crystal.pos), +// crystalColor.compared(this, target ?: return@forEach, crystal.pos) +// ) +// } + + val blurple = Color(85, 57, 204, 50) + testRender.forEach { pos -> + it.renderer.ofShape(pos, blurple, blurple) } testRender.clear() } } - private enum class Page { - Targeting, - Rotation, - Placing, - Exploding, - Rendering +// private fun SafeContext.worldCrystals(target: LivingEntity) = +// fastEntitySearch(explodeRange, pos = target.blockPos) +// .sortedByDescending { explodeMethod.sorted(this, target, it.blockPos) } + + private fun SafeContext.findTargetPositions(target: LivingEntity): List { + // This formula is derived from the explosion damage scaling logic. The damage decreases linearly + // with distance, modeled as `damage = (1 - (distance / (power * 2))) * exposure`, where power is the + // explosion's strength (6.0) and exposure defines how much of the explosion affects the target. + val maximumRange = ((1 - (placeMinDamage / 12.0)) * 12.0).toInt() + + return BlockPos.iterateOutwards(target.blockPos, maximumRange, maximumRange, maximumRange).mapNotNull { pos -> + targetData(pos, pos.blockState(world), target) + }.sortedWith(placeMethod.comparator) + } + + private fun SafeContext.targetData(pos: BlockPos, state: BlockState, target: LivingEntity): TargetPosition? { + val inRange = pos.dist(player.eyePos) < interact.reach + 1 + if (!inRange) return null + val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) + if (!isOfBlock) return null + if (pos in placements) return null + val above = pos.up() + val isAirAbove = world.isAir(above) + if (!isAirAbove) return null + val checkBox = Box(above).withMaxY(above.y + 2.0) + val entitiesAbove = world.getOtherEntities(null, checkBox) + if (entitiesAbove.isNotEmpty()) return null + val crystalPos = above.vec3d.add(0.5, 0.0, 0.5) + val targetDamage = explosionDamage(crystalPos, target, 6.0) + if (targetDamage <= placeMinDamage) return null + val selfDamage = explosionDamage(crystalPos, player, 6.0) + if (selfDamage > placeMaxSelfDamage) return null + return TargetPosition(pos.toImmutable(), targetDamage, selfDamage) } + private val testRender = mutableListOf() + + data class TargetPosition( + val blockPos: BlockPos, + val targetDamage: Double, + val selfDamage: Double, + ) + /** * Damage sorter parameter * * deadly -> always prioritize enemy damage no matter what - * balanced -> sort by the highest ratio of enemy damage to self damage - * safe -> always prioritize the least amount of self damage + * balanced -> sort by the highest ratio of enemy damage to self-damage + * safe -> always prioritize the least amount of self-damage */ - private enum class DamageSort(val sorted: SafeContext.(LivingEntity, BlockPos) -> Double) { - Deadly({ target, source -> explosionDamage(source, target, 6.0) }), - Balanced({ target, source -> explosionDamage(source, player, 6.0) - explosionDamage(source, target, 6.0) }), - Safe({ target, source -> explosionDamage(source, target, 6.0) - explosionDamage(source, player, 6.0) }); + private enum class DamageSort(val comparator: Comparator) { + Deadly(compareByDescending { it.targetDamage }.thenBy { it.selfDamage }), + Balanced(compareByDescending { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }), + Safe(compareBy { it.selfDamage }.thenByDescending { it.targetDamage }); } /** @@ -202,7 +184,6 @@ object CrystalAura : Module( val red = transform(target dist dest, 0.0, explodeRange, 255.0, 0.0).toInt() Color(red, 255-red, 0, crystalAlpha) }), - Damage({ target, dest -> val damage = explosionDamage(dest, target, 6.0) .coerceIn(0.0, target.health.toDouble()) @@ -211,4 +192,13 @@ object CrystalAura : Module( Color(red, 255-red, 0, crystalAlpha) }) } + + private enum class Page { + Targeting, + Placing, + Exploding, + Rotation, + Interaction, + Rendering + } } From 5310b56785fc941076c0eca86337ae6dfeeb02d1 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 06:23:34 +0100 Subject: [PATCH 15/69] Troll PvPers --- common/src/main/kotlin/com/lambda/config/groups/Targeting.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index c6a7c2c35..55b8e2b4f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -32,6 +32,8 @@ import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.ArmorStandEntity import net.minecraft.entity.mob.MobEntity import net.minecraft.entity.passive.PassiveEntity +import net.minecraft.util.Uuids +import java.util.UUID /** * Abstract class representing a targeting mechanism for entities in the game. @@ -155,6 +157,7 @@ abstract class Targeting( */ override fun validate(player: ClientPlayerEntity, entity: LivingEntity): Boolean { if (fov < 180 && player.rotation dist player.eyePos.rotationTo(entity.pos) > fov) return false + if (entity.uuid in illegalTargets) return false return super.validate(player, entity) } @@ -170,6 +173,8 @@ abstract class Targeting( priority.factor(this, it) } } + + val illegalTargets = setOf(UUID.fromString("4f332cd7-cf93-427e-a282-53f45f6bb113")) } /** From bcb6546e958952bb74799ab1dc4269f2cfa67874 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 06:28:48 +0100 Subject: [PATCH 16/69] Get state a bit later --- common/src/main/kotlin/com/lambda/config/groups/Targeting.kt | 2 +- .../kotlin/com/lambda/module/modules/combat/CrystalAura.kt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index 55b8e2b4f..b221cbc8a 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -174,7 +174,7 @@ abstract class Targeting( } } - val illegalTargets = setOf(UUID.fromString("4f332cd7-cf93-427e-a282-53f45f6bb113")) + private val illegalTargets = setOf(UUID.fromString("4f332cd7-cf93-427e-a282-53f45f6bb113")) } /** diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 1afc2c41b..b876850c2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -129,13 +129,14 @@ object CrystalAura : Module( val maximumRange = ((1 - (placeMinDamage / 12.0)) * 12.0).toInt() return BlockPos.iterateOutwards(target.blockPos, maximumRange, maximumRange, maximumRange).mapNotNull { pos -> - targetData(pos, pos.blockState(world), target) + targetData(pos, target) }.sortedWith(placeMethod.comparator) } - private fun SafeContext.targetData(pos: BlockPos, state: BlockState, target: LivingEntity): TargetPosition? { + private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): TargetPosition? { val inRange = pos.dist(player.eyePos) < interact.reach + 1 if (!inRange) return null + val state = pos.blockState(world) val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) if (!isOfBlock) return null if (pos in placements) return null From 8be6c5f4eed7c34d6afab41ff2064684f19d3ba9 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 10 Jan 2025 03:48:35 +0100 Subject: [PATCH 17/69] Fix rotation validity checks and crystal placement --- .../com/lambda/interaction/RotationManager.kt | 17 +++++----- .../interaction/rotation/RotationContext.kt | 8 ++++- .../module/modules/combat/CrystalAura.kt | 32 ++++++++++++++----- .../lambda/module/modules/combat/KillAura.kt | 4 +-- .../lambda/module/modules/movement/Speed.kt | 8 ++--- .../lambda/module/modules/player/Scaffold.kt | 6 ++-- .../com/lambda/task/tasks/BreakBlock.kt | 8 ++--- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 6 ++-- 8 files changed, 56 insertions(+), 33 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index 0de883fb7..4fd11fa66 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -66,21 +66,22 @@ object RotationManager : Loadable { block: RequestRotationBuilder.() -> Unit, ) { val builder = RequestRotationBuilder().apply(block) - var lastCtx: RotationContext? = null + var requested: RotationContext? = null listen(priority, alwaysListen) { event -> builder.onUpdate?.invoke(this, event.context)?.let { context -> if (!context.config.rotate) return@let event.context = context - lastCtx = context + requested = context } } listen { event -> - if (!event.context.config.rotate) return@listen - if (event.context == lastCtx) { - builder.onReceive?.invoke(this, event.context) - } + val context = event.context + if (!context.config.rotate) return@listen + if (context != requested) return@listen + if (!context.isValid) return@listen + builder.onReceive?.invoke(this, context) } } @@ -92,12 +93,12 @@ object RotationManager : Loadable { var onReceive: (SafeContext.(context: RotationContext) -> Unit)? = null @RotationDsl - fun onUpdate(block: SafeContext.(lastContext: RotationContext?) -> RotationContext?) { + fun request(block: SafeContext.(lastContext: RotationContext?) -> RotationContext?) { onUpdate = block } @RotationDsl - fun onReceive(block: SafeContext.(context: RotationContext) -> Unit) { + fun finished(block: SafeContext.(context: RotationContext) -> Unit) { onReceive = block } } diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt index 84dd5fbfd..8cb494222 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt @@ -18,6 +18,8 @@ package com.lambda.interaction.rotation import com.lambda.config.groups.RotationConfig +import com.lambda.interaction.RotationManager +import com.lambda.threading.runSafe import com.lambda.util.world.raycast.RayCastUtils.orMiss import net.minecraft.util.hit.HitResult @@ -27,5 +29,9 @@ data class RotationContext( val hitResult: HitResult? = null, val verify: HitResult.() -> Boolean = { true }, ) { - val isValid: Boolean get() = verify(hitResult.orMiss) + val isValid: Boolean get() = runSafe { + // ToDo: Use proper reach + val result = RotationManager.currentRotation.rayCast(10.0, player.eyePos) + verify(result.orMiss) + } ?: false } diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index b876850c2..b7db70ff5 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -22,9 +22,11 @@ import com.lambda.config.groups.RotationSettings import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.builders.ofShape import com.lambda.interaction.RotationManager.rotate +import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -34,10 +36,10 @@ import com.lambda.util.combat.CombatUtils.explosionDamage import com.lambda.util.math.VecUtils.dist import com.lambda.util.math.VecUtils.vec3d import com.lambda.util.math.transform -import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.entity.LivingEntity import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Vec3d @@ -86,20 +88,34 @@ object CrystalAura : Module( private val placements = LimitedDecayQueue(64, 1000L) private val target: LivingEntity? get() = targeting.target() + private var currentRotation: RotationContext? = null + init { - rotate { - onUpdate { - val targetEntity = target ?: return@onUpdate null - val validPositions = findTargetPositions(targetEntity) + listen { + currentRotation?.let { rotate -> + if (!rotate.isValid) return@let + (rotate.hitResult as? BlockHitResult)?.let { result -> + interaction.interactBlock(player, Hand.MAIN_HAND, result) + placements.add(result.blockPos) + currentRotation = null + } + } + target?.let { tar -> + val validPositions = findTargetPositions(tar).filter { it.blockPos !in placements } testRender.addAll(validPositions.map { it.blockPos }) - - validPositions.firstNotNullOfOrNull { - lookAtBlock(it.blockPos, rotation, interact) + currentRotation = validPositions.firstNotNullOfOrNull { + lookAtBlock(it.blockPos.toImmutable(), rotation, interact) } } } + rotate { + request { + currentRotation + } + } + listen { // worldCrystals(target ?: return@listen).forEach { crystal -> // it.renderer.build( diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index 823960fb3..c2d0b5348 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -113,8 +113,8 @@ object KillAura : Module( init { rotate { - onUpdate { - if (!rotate) return@onUpdate null + request { + if (!rotate) return@request null target?.let { target -> buildRotation(target) diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index e0bae6393..04ab5124d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -135,14 +135,14 @@ object Speed : Module( } rotate(100, alwaysListen = false) { - onUpdate { lastContext -> - if (mode != Mode.GRIM_STRAFE) return@onUpdate null - if (!shouldWork()) return@onUpdate null + request { lastContext -> + if (mode != Mode.GRIM_STRAFE) return@request null + if (!shouldWork()) return@request null var yaw = player.yaw val input = newMovementInput() - if (!input.isInputting) return@onUpdate null + if (!input.isInputting) return@request null run { if (!diagonal) return@run diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index 884f804e4..ce97be565 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -124,10 +124,10 @@ object Scaffold : Module( init { rotate { - onUpdate { + request { lastRotation = null - val info = updatePlaceInfo() ?: return@onUpdate null - val rotation = rotate(info) ?: return@onUpdate null + val info = updatePlaceInfo() ?: return@request null + val rotation = rotate(info) ?: return@request null RotationContext(rotation, rotationConfig) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index f0c5e08d9..2388f16e1 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -81,13 +81,13 @@ class BreakBlock @Ta5kBuilder constructor( init { rotate { - onUpdate { - if (state != State.BREAKING) return@onUpdate null - if (!rotate || ctx.instantBreak) return@onUpdate null + request { + if (state != State.BREAKING) return@request null + if (!rotate || ctx.instantBreak) return@request null lookAtBlock(blockPos, rotation, interact, sides) } - onReceive { context -> + finished { context -> isValid = context.isValid } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index fc5e84332..6856879fa 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -161,9 +161,9 @@ class BuildTask @Ta5kBuilder constructor( } rotate { - onUpdate { - if (currentPlacement == null) return@onUpdate null - if (!build.rotateForPlace) return@onUpdate null + request { + if (currentPlacement == null) return@request null + if (!build.rotateForPlace) return@request null currentPlacement?.rotation } } From 31c2ff8a1adcf44e0ba56dafdd12810f96b4af1c Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 10 Jan 2025 05:22:43 +0100 Subject: [PATCH 18/69] Explosion targeting --- .../module/modules/combat/CrystalAura.kt | 231 ++++++++++++------ 1 file changed, 156 insertions(+), 75 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index b7db70ff5..7d9c43a82 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -24,22 +24,28 @@ import com.lambda.context.SafeContext import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.graphics.renderer.esp.builders.ofShape import com.lambda.interaction.RotationManager.rotate import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.combat.CombatUtils.explosionDamage +import com.lambda.util.math.MathUtils.ceilToInt import com.lambda.util.math.VecUtils.dist import com.lambda.util.math.VecUtils.vec3d import com.lambda.util.math.transform +import com.lambda.util.world.fastEntitySearch +import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.Blocks import net.minecraft.entity.LivingEntity +import net.minecraft.entity.decoration.EndCrystalEntity +import net.minecraft.item.Items import net.minecraft.util.Hand -import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Vec3d @@ -50,29 +56,32 @@ object CrystalAura : Module( description = "Automatically attacks entities with crystals", defaultTags = setOf(ModuleTag.COMBAT), ) { - private val page by setting("Page", Page.Targeting) + private val page by setting("Page", Page.General) + + /* General */ + private val strategy by setting("Strategy", Strategy.ExplodeBeforePlace) { page == Page.General } /* Targeting */ // ToDo: Targeting Range should be reach + crystal range (also based on min damage) private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting } /* Placing */ - private val doPlace by setting("Do Place", true) { page == Page.Placing } - private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing } - private val placeMethod by setting("Place Sort", DamageSort.Deadly) { page == Page.Placing } - //private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing } - private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing } - private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, description = "Delay between crystal placements", unit = "ms") { page == Page.Placing } - private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..36.0, 0.5, description = "Minimum health to place a crystal") { page == Page.Placing } - private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to place a crystal") { page == Page.Placing } - private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, description = "Maximum self damage to place a crystal") { page == Page.Placing } + private val place by setting("Place Crystals", true) { page == Page.Placing } + private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing && place } + private val placeMethod by setting("Place Sort", DamageSort.Deadly) { page == Page.Placing && place } + //private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing && place } + private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, "The minimum space between crystals", " blocks") { page == Page.Placing && place } + private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, "Delay between crystal placements", "ms") { page == Page.Placing && place } + private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..36.0, 0.5, "Minimum health to place a crystal") { page == Page.Placing && place } + private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to place a crystal") { page == Page.Placing && place } + private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, "Maximum self damage to place a crystal") { page == Page.Placing && place } /* Exploding */ - private val doExplode by setting("Do Explode", true) { page == Page.Exploding } - private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, description = "Delay between crystal explosions", unit = "ticks") { page == Page.Exploding } - private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, description = "Range to explode crystals") { page == Page.Exploding } - private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) { page == Page.Exploding } - private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to explode a crystal") { page == Page.Exploding } + private val explode by setting("Explode Crystals", true) { page == Page.Exploding } + private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, "Delay between crystal explosions", " ticks") { page == Page.Exploding && explode } + private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, "Range to explode crystals", " blocks") { page == Page.Exploding && explode } + private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) { page == Page.Exploding && explode } + private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to explode a crystal") { page == Page.Exploding && explode } /* Rotation */ private val rotation = RotationSettings(this) { page == Page.Rotation } @@ -82,10 +91,21 @@ object CrystalAura : Module( /* Rendering */ private val renderCrystals by setting("Render Crystal", true) { page == Page.Rendering } - private val crystalColor by setting("Color Comparator", ColorComparator.Damage) { page == Page.Rendering && renderCrystals } + private val crystalColor by setting("Color Comparator", ColorMode.TargetDamage) { page == Page.Rendering && renderCrystals } private val crystalAlpha by setting("Color Alpha", 128, 0..255) { page == Page.Rendering && renderCrystals } + private enum class Page { + General, + Targeting, + Placing, + Exploding, + Rotation, + Interaction, + Rendering + } + private val placements = LimitedDecayQueue(64, 1000L) + private val placementTargets = mutableListOf() private val target: LivingEntity? get() = targeting.target() private var currentRotation: RotationContext? = null @@ -93,17 +113,21 @@ object CrystalAura : Module( init { listen { currentRotation?.let { rotate -> + if (!place) return@let if (!rotate.isValid) return@let - (rotate.hitResult as? BlockHitResult)?.let { result -> - interaction.interactBlock(player, Hand.MAIN_HAND, result) - placements.add(result.blockPos) - currentRotation = null - } + val blockHit = rotate.hitResult?.blockResult ?: return@let + val inMainHand = player.mainHandStack.item == Items.END_CRYSTAL + val inOffHand = player.offHandStack.item == Items.END_CRYSTAL + if (!inMainHand && !inOffHand) return@let + val hand = if (inMainHand) Hand.MAIN_HAND else Hand.OFF_HAND + interaction.interactBlock(player, hand, blockHit) + placements.add(blockHit.blockPos) + currentRotation = null } target?.let { tar -> - val validPositions = findTargetPositions(tar).filter { it.blockPos !in placements } - testRender.addAll(validPositions.map { it.blockPos }) + val validPositions = findPlacementTargets(tar).filter { it.blockPos !in placements } + placementTargets.addAll(validPositions) currentRotation = validPositions.firstNotNullOfOrNull { lookAtBlock(it.blockPos.toImmutable(), rotation, interact) } @@ -112,44 +136,50 @@ object CrystalAura : Module( rotate { request { + if (!place) return@request null currentRotation } } listen { -// worldCrystals(target ?: return@listen).forEach { crystal -> -// it.renderer.build( -// Box.of(crystal.blockPos.toCenterPos(), 1.0, 1.0, 1.0), -// crystalColor.compared(this, target ?: return@forEach, crystal.pos), -// crystalColor.compared(this, target ?: return@forEach, crystal.pos) -// ) -// } - - val blurple = Color(85, 57, 204, 50) - testRender.forEach { pos -> - it.renderer.ofShape(pos, blurple, blurple) + findExplosionTargets(target ?: return@listen).forEach { crystalExplosion -> + val color = crystalExplosion.color + it.renderer.ofBox(crystalExplosion.crystal.boundingBox, color, color) } - testRender.clear() + placementTargets.forEach { target -> + val color = target.color + it.renderer.ofShape(target.blockPos, color, color) + } + + placementTargets.clear() } } -// private fun SafeContext.worldCrystals(target: LivingEntity) = -// fastEntitySearch(explodeRange, pos = target.blockPos) -// .sortedByDescending { explodeMethod.sorted(this, target, it.blockPos) } + private fun SafeContext.findExplosionTargets(target: LivingEntity): List { + val maximumRange = (1 - (explodeMinDamage / 12.0)) * 12.0 + return fastEntitySearch(maximumRange, target.blockPos) + .filter { it.pos.distanceTo(target.pos) <= maximumRange } + .map { crystal -> + val targetDamage = crystalDamage(crystal.pos, target) + val selfDamage = crystalDamage(crystal.pos, player) + val distance = target.dist(crystal.pos) + ExplosionTarget(crystal, targetDamage, selfDamage, distance) + } + } - private fun SafeContext.findTargetPositions(target: LivingEntity): List { + private fun SafeContext.findPlacementTargets(target: LivingEntity): List { // This formula is derived from the explosion damage scaling logic. The damage decreases linearly // with distance, modeled as `damage = (1 - (distance / (power * 2))) * exposure`, where power is the // explosion's strength (6.0) and exposure defines how much of the explosion affects the target. - val maximumRange = ((1 - (placeMinDamage / 12.0)) * 12.0).toInt() + val maximumRange = ((1 - (placeMinDamage / 12.0)) * 12.0).ceilToInt() return BlockPos.iterateOutwards(target.blockPos, maximumRange, maximumRange, maximumRange).mapNotNull { pos -> targetData(pos, target) }.sortedWith(placeMethod.comparator) } - private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): TargetPosition? { + private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): PlacementTarget? { val inRange = pos.dist(player.eyePos) < interact.reach + 1 if (!inRange) return null val state = pos.blockState(world) @@ -167,16 +197,73 @@ object CrystalAura : Module( if (targetDamage <= placeMinDamage) return null val selfDamage = explosionDamage(crystalPos, player, 6.0) if (selfDamage > placeMaxSelfDamage) return null - return TargetPosition(pos.toImmutable(), targetDamage, selfDamage) + val distance = target.dist(crystalPos) + return PlacementTarget(pos.toImmutable(), targetDamage, selfDamage, distance) } - private val testRender = mutableListOf() - - data class TargetPosition( + /** + * @property blockPos The block position associated with this placement target. + * @property targetDamage The damage inflicted on the target. + * @property selfDamage The damage inflicted on the self due to some actions or interactions. + * @property distanceToTarget The distance to the target from the player or reference point. + */ + data class PlacementTarget( val blockPos: BlockPos, - val targetDamage: Double, - val selfDamage: Double, - ) + override val targetDamage: Double, + override val selfDamage: Double, + override val distanceToTarget: Double, + ) : Target() + + /** + * @property crystal The targeted `EndCrystalEntity` in the context of the explosion. + * @property targetDamage The amount of damage inflicted on the target as part of the explosion. + * @property selfDamage The amount of damage inflicted on the self due to the explosion. + * @property distanceToTarget The distance from the origin point (e.g., player) to the target. + */ + data class ExplosionTarget( + val crystal: EndCrystalEntity, + override val targetDamage: Double, + override val selfDamage: Double, + override val distanceToTarget: Double + ) : Target() + + /** + * Represents a target in a system where damage, distance, and other attributes are evaluated. + * + * @property targetDamage The damage inflicted on the target. + * @property selfDamage The damage inflicted on the self due to some actions or interactions. + * @property distanceToTarget The distance to the target from the player or reference point. + * @property color The dynamically calculated color based on the color mode and associated parameters. + */ + abstract class Target { + abstract val targetDamage: Double + abstract val selfDamage: Double + abstract val distanceToTarget: Double + + val color: Color by lazy { + when (crystalColor) { + ColorMode.TargetDamage -> { + val targetHealth = target?.health?.toDouble() ?: 0.0 + val damage = targetDamage.coerceAtMost(targetHealth) + val red = transform(damage, 0.0, targetHealth, 0.0, 255.0).toInt() + Color(red, 255 - red, 0, crystalAlpha) + } + ColorMode.SelfDamage -> { + runSafe { + val selfHealth = player.health.toDouble() + val damage = selfDamage.coerceAtMost(selfHealth) + val red = transform(damage, 0.0, selfHealth, 0.0, 255.0).toInt() + Color(red, 255 - red, 0, crystalAlpha) + } ?: Color.WHITE + } + ColorMode.Distance -> { + val distance = distanceToTarget.coerceAtMost(explodeRange) + val red = transform(distance, 0.0, explodeRange, 0.0, 255.0).toInt() + Color(red, 0, 255 - red, crystalAlpha) + } + } + } + } /** * Damage sorter parameter @@ -185,37 +272,31 @@ object CrystalAura : Module( * balanced -> sort by the highest ratio of enemy damage to self-damage * safe -> always prioritize the least amount of self-damage */ - private enum class DamageSort(val comparator: Comparator) { - Deadly(compareByDescending { it.targetDamage }.thenBy { it.selfDamage }), - Balanced(compareByDescending { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }), - Safe(compareBy { it.selfDamage }.thenByDescending { it.targetDamage }); + private enum class DamageSort(val comparator: Comparator) { + Deadly(compareByDescending { it.targetDamage }.thenBy { it.selfDamage }), + Balanced(compareByDescending { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }), + Safe(compareBy { it.selfDamage }.thenByDescending { it.targetDamage }); + } + + private enum class Strategy { + ExplodeBeforePlace, + PlaceBeforeExplode, } /** - * Comparator for different render modes + * Represents the different modes of color categorization or operations. * - * @param compared Lambda that takes in a living target and crystal position and then returns a color + * Enum values: + * - TargetDamage: Indicates color mode based on damage to a target. + * - SelfDamage: Indicates color mode based on damage to oneself. + * - Distance: Indicates color mode based on distance criteria. */ - private enum class ColorComparator(val compared: SafeContext.(LivingEntity, Vec3d) -> Color) { - Distance({ target, dest -> - val red = transform(target dist dest, 0.0, explodeRange, 255.0, 0.0).toInt() - Color(red, 255-red, 0, crystalAlpha) - }), - Damage({ target, dest -> - val damage = explosionDamage(dest, target, 6.0) - .coerceIn(0.0, target.health.toDouble()) - - val red = transform(damage, 0.0, target.health.toDouble(), 0.0, 255.0).toInt() - Color(red, 255-red, 0, crystalAlpha) - }) + private enum class ColorMode { + TargetDamage, + SelfDamage, + Distance } - private enum class Page { - Targeting, - Placing, - Exploding, - Rotation, - Interaction, - Rendering - } + private fun SafeContext.crystalDamage(vec3d: Vec3d, target: LivingEntity) = + explosionDamage(vec3d, target, 6.0) } From 178f35a251eb4e23fb5801f85cd9b1bf1d175083 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 10 Jan 2025 05:29:15 +0100 Subject: [PATCH 19/69] Fix missing merge stuff --- .../kotlin/com/lambda/module/modules/combat/CrystalAura.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 7d9c43a82..1e29c9ee5 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -27,7 +27,7 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.graphics.renderer.esp.builders.ofShape import com.lambda.interaction.RotationManager.rotate -import com.lambda.interaction.rotation.RotationContext +import com.lambda.interaction.rotation.RotationRequest import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -108,14 +108,14 @@ object CrystalAura : Module( private val placementTargets = mutableListOf() private val target: LivingEntity? get() = targeting.target() - private var currentRotation: RotationContext? = null + private var currentRotation: RotationRequest? = null init { listen { currentRotation?.let { rotate -> if (!place) return@let if (!rotate.isValid) return@let - val blockHit = rotate.hitResult?.blockResult ?: return@let + val blockHit = rotate.checkedResult?.blockResult ?: return@let val inMainHand = player.mainHandStack.item == Items.END_CRYSTAL val inOffHand = player.offHandStack.item == Items.END_CRYSTAL if (!inMainHand && !inOffHand) return@let From 9dd364a3d14f86a8f507f8787459b7200592cca1 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:50:49 -0500 Subject: [PATCH 20/69] coordinate hud module --- .../com/lambda/config/groups/Targeting.kt | 6 ++- .../com/lambda/module/hud/Coordinates.kt | 46 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index b221cbc8a..a2d5325ce 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -174,7 +174,11 @@ abstract class Targeting( } } - private val illegalTargets = setOf(UUID.fromString("4f332cd7-cf93-427e-a282-53f45f6bb113")) + private val illegalTargets = setOf( + UUID(0L, 0L), + UUID(5706954458220675710L, -6736729783554821869L), + UUID(-6114492090883684892, -8539188786807016414) + ) } /** diff --git a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt new file mode 100644 index 000000000..f49c78114 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.hud + +import com.lambda.context.SafeContext +import com.lambda.module.HudModule +import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runSafe +import com.lambda.util.Formatting.string + +object Coordinates : HudModule( + name = "Coordinates", + description = "Show your coordinates", + defaultTags = setOf(ModuleTag.CLIENT), +) { + private val SafeContext.text: String + get() = "Position: ${player.pos.string}" + + // TODO: Replace by LambdaAtlas height cache and actually build a proper text with highlighted parameters + + override val height: Double get() = 20.0 + override val width: Double get() = 50.0 + + init { + onRender { + runSafe { + font.build(text, position) + } + } + } +} From 4e391c0f267aed1fd84ce18581ca8ec091092981 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:57:07 -0500 Subject: [PATCH 21/69] ref: fakeplayer nil id --- .../kotlin/com/lambda/config/groups/Targeting.kt | 2 -- .../com/lambda/module/modules/combat/FakePlayer.kt | 13 ++++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index a2d5325ce..5415260d2 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -32,7 +32,6 @@ import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.ArmorStandEntity import net.minecraft.entity.mob.MobEntity import net.minecraft.entity.passive.PassiveEntity -import net.minecraft.util.Uuids import java.util.UUID /** @@ -175,7 +174,6 @@ abstract class Targeting( } private val illegalTargets = setOf( - UUID(0L, 0L), UUID(5706954458220675710L, -6736729783554821869L), UUID(-6114492090883684892, -8539188786807016414) ) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index c94e0eaff..c3809ad1c 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -37,22 +37,25 @@ object FakePlayer : Module( private val playerName by setting("Name", "Steve") private var fakePlayer: OtherClientPlayerEntity? = null + private val nilUuid = UUID(0, 0) init { onEnable { - // Avoid multiple api requests - if (fakePlayer?.gameProfile?.name == playerName) - return@onEnable spawnPlayer(fakePlayer!!.gameProfile) + fakePlayer?.let { fake -> + // Avoid multiple api requests + if (fake.gameProfile.name == playerName) + return@onEnable spawnPlayer(fake.gameProfile) + } runSafeConcurrent { val uuid = request("https://api.mojang.com/users/profiles/minecraft/$playerName") { method(Method.GET) - }.json().data?.id ?: UUID(0, 0) + }.json().data?.id ?: nilUuid val fetchedProperties = mc.sessionService.fetchProfile(uuid, true)?.profile?.properties - val profile = GameProfile(UUID(0, 0), playerName).apply { + val profile = GameProfile(nilUuid, playerName).apply { fetchedProperties?.forEach { key, value -> properties.put(key, value) } } From 45ca49faf43e2c126415ce363202d600019f59ff Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 11 Jan 2025 12:45:12 -0500 Subject: [PATCH 22/69] ref: coordinate hud --- .../kotlin/com/lambda/module/hud/Coordinates.kt | 17 ++++++++++++++++- .../main/kotlin/com/lambda/module/hud/TPS.kt | 4 ++-- .../kotlin/com/lambda/util/extension/World.kt | 11 +++++++++++ .../kotlin/com/lambda/util/math/MathUtils.kt | 2 -- .../kotlin/com/lambda/util/math/VecUtils.kt | 3 +++ 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt index f49c78114..5f47d28b5 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt @@ -21,15 +21,24 @@ import com.lambda.context.SafeContext import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe +import com.lambda.util.Formatting.asString import com.lambda.util.Formatting.string +import com.lambda.util.extension.dimensionName +import com.lambda.util.math.VecUtils.netherCoord +import com.lambda.util.math.VecUtils.overworldCoord +import net.minecraft.registry.RegistryKey +import net.minecraft.world.World object Coordinates : HudModule( name = "Coordinates", description = "Show your coordinates", defaultTags = setOf(ModuleTag.CLIENT), ) { + private val showDimension by setting("Show Dimension", true) + private val decimals by setting("Decimals", 2, 0..4, 1) + private val SafeContext.text: String - get() = "Position: ${player.pos.string}" + get() = "XYZ ${if (showDimension) dimensionName else ""} ${positionForDimension(world.registryKey)}" // TODO: Replace by LambdaAtlas height cache and actually build a proper text with highlighted parameters @@ -43,4 +52,10 @@ object Coordinates : HudModule( } } } + + private fun SafeContext.positionForDimension(dimension: RegistryKey) = + when (dimension) { + World.NETHER -> "[${player.netherCoord.asString(decimals)}, ${player.netherCoord.z.string}] ${player.overworldCoord.asString(decimals)}" + else -> "${player.overworldCoord.asString(decimals)} [${player.netherCoord.x.string}, ${player.netherCoord.z.string}]" + } } diff --git a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt index 856a96b2d..fa5b0b683 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt @@ -19,9 +19,9 @@ package com.lambda.module.hud import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag +import com.lambda.util.Formatting.string import com.lambda.util.NamedEnum import com.lambda.util.ServerTPS.averageMSPerTick -import com.lambda.util.math.MathUtils.format object TPS : HudModule( name = "TPS", @@ -30,7 +30,7 @@ object TPS : HudModule( ) { private val format by setting("Tick format", TickFormat.TPS) - private val text: String get() = "${format.displayName}: ${format.output().format(2)}${format.unit}" + private val text: String get() = "${format.displayName}: ${format.output().string}${format.unit}" // TODO: Replace by LambdaAtlas height cache and actually build a proper text with highlighted parameters diff --git a/common/src/main/kotlin/com/lambda/util/extension/World.kt b/common/src/main/kotlin/com/lambda/util/extension/World.kt index de812386a..d48e9bb51 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/World.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/World.kt @@ -36,6 +36,17 @@ import net.minecraft.world.World import java.awt.Color import kotlin.experimental.and +val SafeContext.isOverworld: Boolean get() = world.registryKey == World.OVERWORLD +val SafeContext.isNether: Boolean get() = world.registryKey == World.NETHER +val SafeContext.isEnd: Boolean get() = world.registryKey == World.END +val SafeContext.dimensionName: String + get() = when { + isOverworld -> "Overworld" + isNether -> "Nether" + isEnd -> "End" + else -> "Unknown" + } + fun SafeContext.collisionShape(state: BlockState, pos: BlockPos) = state.getCollisionShape(world, pos).offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) diff --git a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt index d7e08b034..b397cf190 100644 --- a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt @@ -82,7 +82,5 @@ object MathUtils { fun Int.nextPowerOf2() = 2f.pow(ceil(log2(toFloat()))).toInt() - fun Double.format(scale: Int) = "%.${scale}f".format(this) - inline val Int.sq: Int get() = this * this } diff --git a/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt b/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt index 7ef9db79d..f2e48be91 100644 --- a/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt @@ -43,6 +43,9 @@ object VecUtils { fun Vec3d.approximate(other: Vec3d, precision: Double = 2.0E-4): Boolean = (subtract(other) distSq Vec3d.ZERO) > precision.pow(2) + val Entity.netherCoord: Vec3d get() = pos.multiply(0.125, 1.0, 0.125) + val Entity.overworldCoord: Vec3d get() = pos.multiply(8.0, 1.0, 8.0) + infix fun Vec3d.dist(other: Vec3d): Double = sqrt(this distSq other) infix fun Vec3d.dist(other: Vec3i): Double = sqrt(this distSq other) infix fun Vec3d.distSq(other: Vec3d): Double = this.squaredDistanceTo(other) From da168c02d76a791cac1863d1981518f76b7ae5cc Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 12 Jan 2025 05:05:14 +0100 Subject: [PATCH 23/69] Fix LimitedDecayQueue MutableIterator --- .../com/lambda/util/collections/LimitedDecayQueue.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt index 4734065d4..b8610d88f 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt @@ -42,7 +42,17 @@ class LimitedDecayQueue( override fun iterator(): MutableIterator { cleanUp() - return queue.map { it.first }.iterator() as MutableIterator + return object : MutableIterator { + private val delegate = queue.iterator() + + override fun hasNext(): Boolean = delegate.hasNext() + + override fun next(): E = delegate.next().first + + override fun remove() { + delegate.remove() // This affects the underlying queue directly + } + } } @Synchronized From 1bba52a8d9b84acafcd3638da5beee1d2370dcf2 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 12 Jan 2025 05:05:30 +0100 Subject: [PATCH 24/69] CrystalAura exploding --- .../ClientPlayNetworkHandlerMixin.java | 3 + .../lambda/mixin/world/ClientWorldMixin.java | 9 +- .../com/lambda/config/groups/Targeting.kt | 2 +- .../com/lambda/event/events/EntityEvent.kt | 11 + .../interaction/rotation/RotationRequest.kt | 3 +- .../visibilty/VisibilityChecker.kt | 8 +- .../module/modules/combat/CrystalAura.kt | 229 +++++++++++++----- 7 files changed, 198 insertions(+), 67 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java b/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java index 523276933..12267421c 100644 --- a/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java +++ b/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java @@ -19,12 +19,15 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.InventoryEvent; +import com.lambda.event.events.WorldEvent; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket; +import net.minecraft.world.explosion.Explosion; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ClientPlayNetworkHandler.class) diff --git a/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java b/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java index 664997d34..ba292b187 100644 --- a/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java +++ b/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java @@ -33,10 +33,17 @@ @Mixin(ClientWorld.class) public class ClientWorldMixin { @Inject(method = "addEntity", at = @At("HEAD"), cancellable = true) - private void addEntity(Entity entity, CallbackInfo ci) { + private void onAddEntity(Entity entity, CallbackInfo ci) { if (EventFlow.post(new EntityEvent.EntitySpawn(entity)).isCanceled()) ci.cancel(); } + @Inject(method = "removeEntity", at = @At("HEAD")) + private void onRemoveEntity(int entityId, Entity.RemovalReason removalReason, CallbackInfo ci) { + Entity entity = ((ClientWorld) (Object) this).getEntityById(entityId); + if (entity == null) return; + EventFlow.post(new EntityEvent.EntityRemoval(entity, removalReason)); + } + @Inject(method = "getCloudsColor", at = @At("HEAD"), cancellable = true) private void getCloudsColorInject(float tickDelta, CallbackInfoReturnable cir) { if (WorldColors.INSTANCE.isEnabled() && WorldColors.getCustomClouds()) { diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index 5415260d2..282cbcaa0 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -174,7 +174,7 @@ abstract class Targeting( } private val illegalTargets = setOf( - UUID(5706954458220675710L, -6736729783554821869L), + UUID(5706954458220675710, -6736729783554821869), UUID(-6114492090883684892, -8539188786807016414) ) } diff --git a/common/src/main/kotlin/com/lambda/event/events/EntityEvent.kt b/common/src/main/kotlin/com/lambda/event/events/EntityEvent.kt index 219c5397e..fc608b700 100644 --- a/common/src/main/kotlin/com/lambda/event/events/EntityEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/EntityEvent.kt @@ -54,6 +54,17 @@ sealed class EntityEvent { val entity: Entity, ) : ICancellable by Cancellable() + /** + * Represents an event triggered when an entity is removed from the game world. + * + * @property entity The entity being removed from the world. + * @property removalReason The reason for the removal of the entity. + */ + data class EntityRemoval( + val entity: Entity, + val removalReason: Entity.RemovalReason, + ) : Event + /** * Represents an event triggered when an entity's tracked data is updated. * diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt index 5116fa6e8..28d1ab7d2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt @@ -29,9 +29,10 @@ data class RotationRequest( val checkedResult: HitResult? = null, val verify: HitResult.() -> Boolean = { true }, ) { + val cast: HitResult? get() = runSafe { RotationManager.currentRotation.rayCast(10.0, player.eyePos) } val isValid: Boolean get() = runSafe { // ToDo: Use proper reach - val result = RotationManager.currentRotation.rayCast(10.0, player.eyePos) + val result = cast verify(result.orMiss) } ?: false } diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt index 6489517b5..44e349f80 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -55,9 +55,9 @@ object VisibilityChecker { * @return A [RotationRequest] if a valid rotation was found; otherwise, null. */ fun SafeContext.lookAtEntity( - rotationConfig: RotationConfig, - interactionConfig: InteractionConfig, entity: Entity, + rotationConfig: RotationConfig = TaskFlowModule.rotation, + interactionConfig: InteractionConfig = TaskFlowModule.interact, ) = findRotation(listOf(entity.boundingBox), rotationConfig, interactionConfig) { entityResult?.entity == entity } @@ -100,8 +100,8 @@ object VisibilityChecker { */ fun SafeContext.findRotation( boxes: List, - rotationConfig: RotationConfig, - interact: InteractionConfig, + rotationConfig: RotationConfig = TaskFlowModule.rotation, + interact: InteractionConfig = TaskFlowModule.interact, sides: Set = Direction.entries.toSet(), reach: Double = interact.reach, eye: Vec3d = player.getCameraPosVec(1f), diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 1e29c9ee5..e20d083ef 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -21,21 +21,26 @@ import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.RotationSettings import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext +import com.lambda.event.events.EntityEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent +import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.builders.ofBox import com.lambda.graphics.renderer.esp.builders.ofShape import com.lambda.interaction.RotationManager.rotate import com.lambda.interaction.rotation.RotationRequest import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock +import com.lambda.interaction.visibilty.VisibilityChecker.lookAtEntity import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState +import com.lambda.util.Communication.info import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.combat.CombatUtils.explosionDamage import com.lambda.util.math.MathUtils.ceilToInt +import com.lambda.util.math.VecUtils.blockPos import com.lambda.util.math.VecUtils.dist import com.lambda.util.math.VecUtils.vec3d import com.lambda.util.math.transform @@ -67,9 +72,11 @@ object CrystalAura : Module( /* Placing */ private val place by setting("Place Crystals", true) { page == Page.Placing } - private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing && place } + private val rotateToPlace by setting("Rotate To Place", true) { page == Page.Placing && place } + private val placementTimeout: Long by setting("Placement Timeout", 500L, 50L..2000L, 10L, "The timeout in milliseconds for placing a crystal", " ms") { page == Page.Placing && place }.apply { onValueSet { _, to -> pendingPlacements.setDecayTime(to) } } + private val maxPendingPlacements by setting("Max Pending Placements", 1, 1..20, 1, "The maximum number of pending placements to keep track of") { page == Page.Placing && place } + private val swap by setting("Swap", Hand.MAIN_HAND, "Automatically swap to place crystals") { page == Page.Placing && place } private val placeMethod by setting("Place Sort", DamageSort.Deadly) { page == Page.Placing && place } - //private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing && place } private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, "The minimum space between crystals", " blocks") { page == Page.Placing && place } private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, "Delay between crystal placements", "ms") { page == Page.Placing && place } private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..36.0, 0.5, "Minimum health to place a crystal") { page == Page.Placing && place } @@ -78,7 +85,10 @@ object CrystalAura : Module( /* Exploding */ private val explode by setting("Explode Crystals", true) { page == Page.Exploding } - private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, "Delay between crystal explosions", " ticks") { page == Page.Exploding && explode } + private val rotateToExplode by setting("Rotate To Explode", false) { page == Page.Exploding && explode } + private val explosionTimeout: Long by setting("Explosion Timeout", 500L, 50L..2000L, 10L, "The timeout in milliseconds for exploding a crystal", " ms") { page == Page.Exploding && explode }.apply { onValueSet { _, to -> pendingExplosions.setDecayTime(to) } } + private val maxPendingExplosions by setting("Max Pending Explosions", 1, 1..20, 1, "The maximum number of pending explosions to keep track of") { page == Page.Exploding && explode } + private val explosionDelay by setting("Explode Delay", 0, 0..20, 1, "Delay between crystal explosions", " ticks") { page == Page.Exploding && explode } private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, "Range to explode crystals", " blocks") { page == Page.Exploding && explode } private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) { page == Page.Exploding && explode } private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to explode a crystal") { page == Page.Exploding && explode } @@ -91,8 +101,9 @@ object CrystalAura : Module( /* Rendering */ private val renderCrystals by setting("Render Crystal", true) { page == Page.Rendering } - private val crystalColor by setting("Color Comparator", ColorMode.TargetDamage) { page == Page.Rendering && renderCrystals } + private val crystalColor by setting("Color", ColorMode.TargetDamage) { page == Page.Rendering && renderCrystals } private val crystalAlpha by setting("Color Alpha", 128, 0..255) { page == Page.Rendering && renderCrystals } + private val scalingFactor by setting("Scaling Factor", 1.0, 0.0..2.0, 0.1, "The scaling factor for the color mode") { page == Page.Rendering && renderCrystals } private enum class Page { General, @@ -104,71 +115,149 @@ object CrystalAura : Module( Rendering } - private val placements = LimitedDecayQueue(64, 1000L) - private val placementTargets = mutableListOf() private val target: LivingEntity? get() = targeting.target() - private var currentRotation: RotationRequest? = null + private val pendingPlacements = LimitedDecayQueue(64, placementTimeout) + private val possiblePlaceOpportunities = mutableListOf() + + private val pendingExplosions = LimitedDecayQueue(64, explosionTimeout) + private val possibleExplodeOpportunities = mutableListOf() + + private var explosionRequest: ExplosionOpportunity? = null + private var placementRequest: PlacementOpportunity? = null + private var rotationRequest: RotationRequest? = null init { listen { - currentRotation?.let { rotate -> - if (!place) return@let - if (!rotate.isValid) return@let - val blockHit = rotate.checkedResult?.blockResult ?: return@let - val inMainHand = player.mainHandStack.item == Items.END_CRYSTAL - val inOffHand = player.offHandStack.item == Items.END_CRYSTAL - if (!inMainHand && !inOffHand) return@let - val hand = if (inMainHand) Hand.MAIN_HAND else Hand.OFF_HAND - interaction.interactBlock(player, hand, blockHit) - placements.add(blockHit.blockPos) - currentRotation = null + explodeCrystal() + placeCrystal() + determineActionForNextTick() + } + + rotate { request { rotationRequest } } + + listen { event -> + pendingExplosions.removeAll { it.crystal.id == event.entity.id } + } + + listen { event -> + pendingPlacements.removeAll { it.blockPos.up() == event.entity.blockPos} + } + + listen { + possibleExplodeOpportunities.forEach { opportunity -> + val color = opportunity.color + it.renderer.ofBox(opportunity.crystal.boundingBox, color, color) } - target?.let { tar -> - val validPositions = findPlacementTargets(tar).filter { it.blockPos !in placements } - placementTargets.addAll(validPositions) - currentRotation = validPositions.firstNotNullOfOrNull { - lookAtBlock(it.blockPos.toImmutable(), rotation, interact) - } + possiblePlaceOpportunities.forEach { opportunity -> + val color = opportunity.color + it.renderer.ofShape(opportunity.blockPos, color, color) } + + possibleExplodeOpportunities.clear() + possiblePlaceOpportunities.clear() } + } + + private fun SafeContext.determineActionForNextTick() { + target?.let { tar -> + collectExplosions(tar) + collectPlacements(tar) + + val bestPlacement = possiblePlaceOpportunities.firstNotNullOfOrNull { opportunity -> + lookAtBlock(opportunity.blockPos, rotation, interact) to opportunity + } + + val bestExplosion = possibleExplodeOpportunities.firstNotNullOfOrNull { opportunity -> + lookAtEntity(opportunity.crystal, rotation, interact) to opportunity + } - rotate { - request { - if (!place) return@request null - currentRotation + if (explode + && pendingExplosions.size <= maxPendingExplosions + && bestExplosion != null + && (bestPlacement == null || bestExplosion.second.isBest(bestPlacement.second)) + ) { + if (rotateToExplode) rotationRequest = bestExplosion.first + explosionRequest = bestExplosion.second + return } + + if (place + && pendingPlacements.size <= maxPendingPlacements + && bestPlacement != null + && (bestExplosion == null || bestPlacement.second.isBest(bestExplosion.second)) + ) { + if (rotateToPlace) rotationRequest = bestPlacement.first + placementRequest = bestPlacement.second + return + } + + placementRequest = null + explosionRequest = null + rotationRequest = null } + } - listen { - findExplosionTargets(target ?: return@listen).forEach { crystalExplosion -> - val color = crystalExplosion.color - it.renderer.ofBox(crystalExplosion.crystal.boundingBox, color, color) + private fun ExplosionOpportunity.isBest(placement: PlacementOpportunity) = + targetDamage >= placement.targetDamage && pendingExplosions.none { it.targetDamage >= targetDamage } + + private fun PlacementOpportunity.isBest(explosion: ExplosionOpportunity) = + targetDamage >= explosion.targetDamage && pendingPlacements.none { it.targetDamage >= targetDamage } + + private fun SafeContext.placeCrystal() { + getNextPlacement { request, placement -> + if (!place || (!request.isValid && rotateToPlace)) return@getNextPlacement + + val inMainHand = player.mainHandStack.item == Items.END_CRYSTAL + val inOffHand = player.offHandStack.item == Items.END_CRYSTAL + if (!inMainHand && !inOffHand) return@getNextPlacement + + val hand = if (inMainHand) Hand.MAIN_HAND else Hand.OFF_HAND + + val hitResult = request.cast?.blockResult ?: return@getNextPlacement + val actionResult = interaction.interactBlock(player, hand, hitResult) + if (!actionResult.isAccepted) return@getNextPlacement + + if (actionResult.shouldSwingHand() && interact.swingHand) { + player.swingHand(hand) } - placementTargets.forEach { target -> - val color = target.color - it.renderer.ofShape(target.blockPos, color, color) +// info("Placed at ${placement.blockPos.toShortString()} with target damage ${placement.targetDamage} and self damage ${placement.selfDamage} at ${placement.distanceToTarget}m") + pendingPlacements.add(placement) + placementRequest = null + rotationRequest = null + } + } + + private fun SafeContext.explodeCrystal() { + explosionRequest?.let { opportunity -> + if (!explode || (rotationRequest?.isValid == false && rotateToExplode)) return + + interaction.attackEntity(player, opportunity.crystal) + if (interact.swingHand) { + player.swingHand(Hand.MAIN_HAND) } - placementTargets.clear() +// info("Exploded ${opportunity.crystal.name.string} at ${opportunity.crystal.pos} with target damage ${opportunity.targetDamage} and self damage ${opportunity.selfDamage} at ${opportunity.distanceToTarget}m") + pendingExplosions.add(opportunity) + explosionRequest = null + rotationRequest = null } } - private fun SafeContext.findExplosionTargets(target: LivingEntity): List { + private fun SafeContext.findExplosionOpportunities(target: LivingEntity): List { val maximumRange = (1 - (explodeMinDamage / 12.0)) * 12.0 return fastEntitySearch(maximumRange, target.blockPos) - .filter { it.pos.distanceTo(target.pos) <= maximumRange } .map { crystal -> val targetDamage = crystalDamage(crystal.pos, target) val selfDamage = crystalDamage(crystal.pos, player) val distance = target.dist(crystal.pos) - ExplosionTarget(crystal, targetDamage, selfDamage, distance) - } + ExplosionOpportunity(crystal, targetDamage, selfDamage, distance) + }.filterNot { it in pendingExplosions }.sortedWith(explodeMethod.comparator) } - private fun SafeContext.findPlacementTargets(target: LivingEntity): List { + private fun SafeContext.findPlacementOpportunities(target: LivingEntity): List { // This formula is derived from the explosion damage scaling logic. The damage decreases linearly // with distance, modeled as `damage = (1 - (distance / (power * 2))) * exposure`, where power is the // explosion's strength (6.0) and exposure defines how much of the explosion affects the target. @@ -176,30 +265,49 @@ object CrystalAura : Module( return BlockPos.iterateOutwards(target.blockPos, maximumRange, maximumRange, maximumRange).mapNotNull { pos -> targetData(pos, target) - }.sortedWith(placeMethod.comparator) + }.filterNot { it in pendingPlacements }.sortedWith(placeMethod.comparator) } - private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): PlacementTarget? { + private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): PlacementOpportunity? { val inRange = pos.dist(player.eyePos) < interact.reach + 1 if (!inRange) return null + val state = pos.blockState(world) val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) if (!isOfBlock) return null - if (pos in placements) return null + + if (pendingPlacements.any { it.blockPos == pos }) return null + val above = pos.up() val isAirAbove = world.isAir(above) if (!isAirAbove) return null + val checkBox = Box(above).withMaxY(above.y + 2.0) val entitiesAbove = world.getOtherEntities(null, checkBox) if (entitiesAbove.isNotEmpty()) return null + val crystalPos = above.vec3d.add(0.5, 0.0, 0.5) val targetDamage = explosionDamage(crystalPos, target, 6.0) if (targetDamage <= placeMinDamage) return null + val selfDamage = explosionDamage(crystalPos, player, 6.0) if (selfDamage > placeMaxSelfDamage) return null val distance = target.dist(crystalPos) - return PlacementTarget(pos.toImmutable(), targetDamage, selfDamage, distance) + + return PlacementOpportunity(pos.toImmutable(), targetDamage, selfDamage, distance) + } + + private fun SafeContext.collectExplosions(target: LivingEntity) { + val targets = findExplosionOpportunities(target) + possibleExplodeOpportunities.addAll(targets) + } + + private fun SafeContext.collectPlacements(target: LivingEntity) { + val targets = findPlacementOpportunities(target) + possiblePlaceOpportunities.addAll(targets) } + private fun getNextPlacement(block: (RotationRequest, PlacementOpportunity) -> Unit) = + rotationRequest?.let { rot -> placementRequest?.let { explode -> block(rot, explode) }} /** * @property blockPos The block position associated with this placement target. @@ -207,12 +315,12 @@ object CrystalAura : Module( * @property selfDamage The damage inflicted on the self due to some actions or interactions. * @property distanceToTarget The distance to the target from the player or reference point. */ - data class PlacementTarget( + data class PlacementOpportunity( val blockPos: BlockPos, override val targetDamage: Double, override val selfDamage: Double, override val distanceToTarget: Double, - ) : Target() + ) : Opportunity() /** * @property crystal The targeted `EndCrystalEntity` in the context of the explosion. @@ -220,22 +328,22 @@ object CrystalAura : Module( * @property selfDamage The amount of damage inflicted on the self due to the explosion. * @property distanceToTarget The distance from the origin point (e.g., player) to the target. */ - data class ExplosionTarget( + data class ExplosionOpportunity( val crystal: EndCrystalEntity, override val targetDamage: Double, override val selfDamage: Double, override val distanceToTarget: Double - ) : Target() + ) : Opportunity() /** - * Represents a target in a system where damage, distance, and other attributes are evaluated. + * Represents an opportunity to cause damage. * * @property targetDamage The damage inflicted on the target. * @property selfDamage The damage inflicted on the self due to some actions or interactions. * @property distanceToTarget The distance to the target from the player or reference point. * @property color The dynamically calculated color based on the color mode and associated parameters. */ - abstract class Target { + abstract class Opportunity { abstract val targetDamage: Double abstract val selfDamage: Double abstract val distanceToTarget: Double @@ -243,10 +351,11 @@ object CrystalAura : Module( val color: Color by lazy { when (crystalColor) { ColorMode.TargetDamage -> { - val targetHealth = target?.health?.toDouble() ?: 0.0 - val damage = targetDamage.coerceAtMost(targetHealth) - val red = transform(damage, 0.0, targetHealth, 0.0, 255.0).toInt() - Color(red, 255 - red, 0, crystalAlpha) + val targetHealth = target?.health?.toDouble()?.takeIf { it > 0.0 } ?: 20.0 + val normalizedDamage = (targetDamage / targetHealth).coerceIn(0.0, 1.0) + val adjustedRed = (normalizedDamage * 255.0).toInt().coerceIn(0, 255) + val adjustedGreen = (255 - adjustedRed).coerceIn(0, 255) + Color(adjustedRed, adjustedGreen, 0, crystalAlpha) } ColorMode.SelfDamage -> { runSafe { @@ -272,15 +381,15 @@ object CrystalAura : Module( * balanced -> sort by the highest ratio of enemy damage to self-damage * safe -> always prioritize the least amount of self-damage */ - private enum class DamageSort(val comparator: Comparator) { - Deadly(compareByDescending { it.targetDamage }.thenBy { it.selfDamage }), - Balanced(compareByDescending { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }), - Safe(compareBy { it.selfDamage }.thenByDescending { it.targetDamage }); + private enum class DamageSort(val comparator: Comparator) { + Deadly(compareByDescending { it.targetDamage }.thenBy { it.selfDamage }), + Balanced(compareByDescending { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }), + Safe(compareBy { it.selfDamage }.thenByDescending { it.targetDamage }); } private enum class Strategy { ExplodeBeforePlace, - PlaceBeforeExplode, + PlaceBeforeExplode } /** From 5d7a70645e98ad334780fbc23665c70883aad494 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:30:08 -0500 Subject: [PATCH 25/69] fix: fake player targeting --- common/src/main/kotlin/com/lambda/config/groups/Targeting.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index 282cbcaa0..d3820b808 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -113,7 +113,7 @@ abstract class Targeting( * @return `true` if the entity is valid for targeting, `false` otherwise. */ open fun validate(player: ClientPlayerEntity, entity: LivingEntity) = when { - !players && (entity is OtherClientPlayerEntity && entity.isFriend) -> false + !players || (entity is OtherClientPlayerEntity && entity.isFriend) -> false !animals && entity is PassiveEntity -> false !hostiles && entity is MobEntity -> false entity is ArmorStandEntity -> false @@ -174,8 +174,7 @@ abstract class Targeting( } private val illegalTargets = setOf( - UUID(5706954458220675710, -6736729783554821869), - UUID(-6114492090883684892, -8539188786807016414) + UUID(5706954458220675710, -6736729783554821869) ) } From ac6054b8afe5ffb08ec6449eef490151243cfedd Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:31:11 -0500 Subject: [PATCH 26/69] ref: use mc's reach --- .../kotlin/com/lambda/config/groups/InteractionConfig.kt | 5 +++++ .../com/lambda/config/groups/InteractionSettings.kt | 8 ++++++-- .../kotlin/com/lambda/module/modules/combat/KillAura.kt | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt index 4b1a946fb..d4ed1337c 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt @@ -20,6 +20,11 @@ package com.lambda.config.groups import com.lambda.core.PingManager interface InteractionConfig { + /** + * Minecraft's default reach values + */ + val defaultReach: Boolean + /** * Maximum distance to interact. */ diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index e528607af..ae31d7134 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -18,13 +18,17 @@ package com.lambda.config.groups import com.lambda.config.Configurable +import com.lambda.threading.runSafe +import net.minecraft.entity.player.PlayerEntity class InteractionSettings( c: Configurable, - defaultReach: Double = 4.6, vis: () -> Boolean = { true }, ) : InteractionConfig { - override val reach by c.setting("Reach", defaultReach, 0.1..10.0, 0.1, "Players reach / range", " blocks", vis) + override val defaultReach by c.setting("Default Reach", true) + private val customReach by c.setting("Reach", 4.5, 0.1..10.0, 0.1, "Players reach / range", " blocks") { vis() && !defaultReach } + override val reach: Double + get() = if (defaultReach) runSafe { PlayerEntity.getReachDistance(interaction.currentGameMode.isCreative).toDouble() } ?: 4.5 else customReach override val useRayCast by c.setting("Raycast", true, "Verify hit vector with ray casting (for very strict ACs)", vis) override val visibilityCheck by c.setting("Visibility Check", true, "Check if target is visible", vis) override val resolution by c.setting("Resolution", 4, 1..40, 1, "How many raycast checks per surface (will be squared)") { vis() && useRayCast } diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index b203e2f72..762343694 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -62,7 +62,7 @@ object KillAura : Module( private val page by setting("Page", Page.Interact) // Interact - private val interactionSettings = InteractionSettings(this, 3.0) { page == Page.Interact } + private val interactionSettings = InteractionSettings(this) { page == Page.Interact } private val attackMode by setting("Attack Mode", AttackMode.Cooldown) { page == Page.Interact } private val delaySync by setting("Client-side Delay", true) { page == Page.Interact && attackMode == AttackMode.Cooldown } private val cooldownSync by setting("Client-side Cooldown", true) { page == Page.Interact && attackMode == AttackMode.Cooldown } From 4d7df220a7a61ddb2225059b54ccbc6d5f28d289 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:32:33 -0500 Subject: [PATCH 27/69] ref: raycast arguments --- .../main/kotlin/com/lambda/interaction/rotation/Rotation.kt | 4 ++++ .../main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt index 3bf05d1f5..3213d8ed8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt @@ -42,6 +42,10 @@ data class Rotation(val yaw: Double, val pitch: Double) { fun equalFloat(other: Rotation): Boolean = yawF == other.yawF && pitchF == other.pitchF + /** + * Returns the player's rotation vector + * Same as [net.minecraft.entity.Entity.getRotationVec] + */ val vector: Vec3d get() { val yawRad = -yaw.toRadian() diff --git a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt index 0196f6271..e4dfa61c2 100644 --- a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt @@ -40,12 +40,12 @@ object RayCastUtils { fun SafeContext.rayCast( start: Vec3d, - end: Vec3d, + rotation: Vec3d, reach: Double, mask: RayCastMask, fluids: Boolean = false, ): HitResult? { - val vec = end.multiply(reach) + val vec = rotation.multiply(reach) val point = start.add(vec) val block = run { From bd40575bae0aab4d6714afaa0d1b9ddce05b44aa Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:41:17 -0500 Subject: [PATCH 28/69] disable fake player on shutdown --- .../main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index c3809ad1c..b0c3a19eb 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -22,6 +22,7 @@ import com.lambda.http.Method import com.lambda.http.request import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.threading.onShutdown import com.lambda.threading.runSafeConcurrent import com.mojang.authlib.GameProfile import net.minecraft.client.network.OtherClientPlayerEntity @@ -68,6 +69,8 @@ object FakePlayer : Module( onDisable { deletePlayer() } + + onShutdown { disable() } } private fun SafeContext.spawnPlayer(profile: GameProfile) { From f218933efb1faf5b98630fa14f86892744a91025 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:41:36 -0500 Subject: [PATCH 29/69] clear caura on disable --- .../lambda/module/modules/combat/CrystalAura.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index e20d083ef..b68bb2b76 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -21,6 +21,7 @@ import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.RotationSettings import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext +import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.EntityEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent @@ -158,6 +159,20 @@ object CrystalAura : Module( possibleExplodeOpportunities.clear() possiblePlaceOpportunities.clear() } + + onDisable { clear() } + listen { clear() } + } + + private fun clear() { + pendingPlacements.clear() + possiblePlaceOpportunities.clear() + pendingExplosions.clear() + possibleExplodeOpportunities.clear() + + placementRequest = null + explosionRequest = null + rotationRequest = null } private fun SafeContext.determineActionForNextTick() { From 3af2f4102cf58ad250ff83028d579fcacab48da4 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:49:01 -0500 Subject: [PATCH 30/69] fix: kill aura reach --- .../kotlin/com/lambda/config/groups/InteractionSettings.kt | 7 +++++-- .../kotlin/com/lambda/module/modules/combat/KillAura.kt | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index ae31d7134..ff567e318 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -21,12 +21,15 @@ import com.lambda.config.Configurable import com.lambda.threading.runSafe import net.minecraft.entity.player.PlayerEntity +// TODO: Rewrite the group settings plz class InteractionSettings( c: Configurable, + useDefaultReach: Boolean = true, + cReach: Double = 3.0, vis: () -> Boolean = { true }, ) : InteractionConfig { - override val defaultReach by c.setting("Default Reach", true) - private val customReach by c.setting("Reach", 4.5, 0.1..10.0, 0.1, "Players reach / range", " blocks") { vis() && !defaultReach } + override val defaultReach by c.setting("Default Reach", useDefaultReach) + private val customReach by c.setting("Reach", if (!defaultReach) cReach else 4.5, 0.1..10.0, 0.1, "Players reach / range", " blocks") { vis() && !defaultReach } override val reach: Double get() = if (defaultReach) runSafe { PlayerEntity.getReachDistance(interaction.currentGameMode.isCreative).toDouble() } ?: 4.5 else customReach override val useRayCast by c.setting("Raycast", true, "Verify hit vector with ray casting (for very strict ACs)", vis) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index 762343694..5d6368de1 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -62,7 +62,7 @@ object KillAura : Module( private val page by setting("Page", Page.Interact) // Interact - private val interactionSettings = InteractionSettings(this) { page == Page.Interact } + private val interactionSettings = InteractionSettings(this, useDefaultReach = false) { page == Page.Interact } private val attackMode by setting("Attack Mode", AttackMode.Cooldown) { page == Page.Interact } private val delaySync by setting("Client-side Delay", true) { page == Page.Interact && attackMode == AttackMode.Cooldown } private val cooldownSync by setting("Client-side Cooldown", true) { page == Page.Interact && attackMode == AttackMode.Cooldown } From 156d16c722864205fc0b956a89c63236cd75348d Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 13 Jan 2025 01:51:56 +0100 Subject: [PATCH 31/69] Disable FakePlayer on disconnect --- .../kotlin/com/lambda/module/modules/combat/FakePlayer.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index b0c3a19eb..62bd9c2d9 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -18,6 +18,8 @@ package com.lambda.module.modules.combat import com.lambda.context.SafeContext +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.http.Method import com.lambda.http.request import com.lambda.module.Module @@ -71,6 +73,10 @@ object FakePlayer : Module( } onShutdown { disable() } + + listen { + disable() + } } private fun SafeContext.spawnPlayer(profile: GameProfile) { From 64218f18e6ad46ea7f9d52fa5ddec3876eba59b7 Mon Sep 17 00:00:00 2001 From: blade Date: Thu, 16 Jan 2025 02:41:58 +0300 Subject: [PATCH 32/69] Timer (for delays and periodic actions) --- .../kotlin/com/lambda/util/SimpleTimer.kt | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 common/src/main/kotlin/com/lambda/util/SimpleTimer.kt diff --git a/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt b/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt new file mode 100644 index 000000000..6a4fa6dd5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.util + +/** + * A utility class to manage time-based operations, such as delays and periodic tasks. + */ +class SimpleTimer { + /** + * Stores the timestamp of the last timing event. + */ + private var lastTiming = 0L + + /** + * Checks if the specified amount of time has passed since the last timing event. + * + * @param time the time interval in milliseconds to check. + * @return `true` if the current time exceeds `lastTiming + time`, `false` otherwise. + */ + fun timePassed(time: Long): Boolean = + currentTime - lastTiming > time + + /** + * Checks if the specified time has passed and resets the timer if true. + * + * @param time the time interval in milliseconds to check. + * @return `true` if the time has passed and the timer was reset, `false` otherwise. + */ + fun delayIfPassed(time: Long): Boolean = + timePassed(time).apply { + if (this) reset() + } + + /** + * Executes a given block of code if the specified time has passed since the last timing event. + * Optionally resets the timer after execution. + * + * @param time the time interval in milliseconds to check. + * @param reset whether to reset the timer after running the block. Defaults to `true`. + * @param block the code block to execute if the time has passed. + */ + fun runIfPassed(time: Long, reset: Boolean = true, block: () -> Unit) = + timePassed(time).apply { + if (!this) return@apply + if (reset) reset() + + block() + } + + /** + * Resets the timer by updating the last timing event to the current time. + * + * @param additionalDelay an additional delay in milliseconds to add to the current time. Defaults to `0`. + */ + fun reset(additionalDelay: Long = 0L) { + lastTiming = currentTime + additionalDelay + } + + companion object { + private val currentTime get() = System.currentTimeMillis() + } +} \ No newline at end of file From 3fd362665c76fe55e5ae389167fbc67992815218 Mon Sep 17 00:00:00 2001 From: blade Date: Thu, 16 Jan 2025 03:23:30 +0300 Subject: [PATCH 33/69] FakePlayer crash fix --- .../kotlin/com/lambda/module/modules/combat/FakePlayer.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index 62bd9c2d9..09c4962cf 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -25,7 +25,9 @@ import com.lambda.http.request import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.onShutdown +import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafeConcurrent +import com.lambda.threading.runSafeGameScheduled import com.mojang.authlib.GameProfile import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry @@ -88,7 +90,9 @@ object FakePlayer : Module( id = -2024 - 4 - 20 } - world.addEntity(fakePlayer) + runSafeGameScheduled { + world.addEntity(fakePlayer) + } } private fun deletePlayer() { From ae4e40f6ce1e9f9bbaaafba7ae2ebb21af604de9 Mon Sep 17 00:00:00 2001 From: blade Date: Fri, 17 Jan 2025 21:39:24 +0300 Subject: [PATCH 34/69] async crystal aura --- .../construction/blueprint/Blueprint.kt | 4 +- .../container/containers/StashContainer.kt | 4 +- .../visibilty/VisibilityChecker.kt | 11 +- .../module/modules/combat/CrystalAura.kt | 475 +++++++----------- .../kotlin/com/lambda/threading/Threading.kt | 2 +- .../kotlin/com/lambda/util/Communication.kt | 6 +- .../kotlin/com/lambda/util/math/VecUtils.kt | 5 +- .../lambda/util/world/raycast/RayCastUtils.kt | 4 +- .../src/main/resources/lambda.accesswidener | 2 + 9 files changed, 195 insertions(+), 318 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt index 0e5f74e46..d0a108e28 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt @@ -21,7 +21,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.BlockUtils.blockPos import com.lambda.util.collections.ResettableLazy import com.lambda.util.extension.Structure -import com.lambda.util.math.VecUtils.blockPos +import com.lambda.util.math.VecUtils.roundedBlockPos import net.minecraft.structure.StructureTemplate import net.minecraft.util.math.* @@ -47,7 +47,7 @@ abstract class Blueprint { return Vec3d(d, e, f) } - fun isOutOfBounds(vec3d: Vec3d): Boolean = bounds.value?.contains(vec3d.blockPos) == false + fun isOutOfBounds(vec3d: Vec3d): Boolean = bounds.value?.contains(vec3d.roundedBlockPos) == false val center get() = bounds.value?.center?.blockPos diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt index 647f2e7a5..a82d27699 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt @@ -19,7 +19,7 @@ package com.lambda.interaction.material.container.containers import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.math.VecUtils.blockPos +import com.lambda.util.math.VecUtils.roundedBlockPos import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal @@ -36,7 +36,7 @@ data class StashContainer( override val description = buildText { literal("Stash at ") - highlighted(pos.center.blockPos.toShortString()) + highlighted(pos.center.roundedBlockPos.toShortString()) } override fun materialAvailable(selection: StackSelection): Int = diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt index 44e349f80..f060cba9e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -23,6 +23,7 @@ import com.lambda.context.SafeContext import com.lambda.interaction.RotationManager import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan +import com.lambda.interaction.rotation.Rotation.Companion.dist import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationRequest import com.lambda.module.modules.client.TaskFlowModule @@ -133,16 +134,16 @@ object VisibilityChecker { } // Way stable - /*validHits.minByOrNull { eye.rotationTo(it.key) dist currentRotation }?.let { closest -> - return RotationContext(eye.rotationTo(closest.key), rotationConfig, closest.value, verify) - }*/ + validHits.minByOrNull { eye.rotationTo(it.key) dist currentRotation }?.let { closest -> + return RotationRequest(eye.rotationTo(closest.key), rotationConfig, closest.value, verify) + } - validHits.keys.optimum?.let { optimum -> + /*validHits.keys.optimum?.let { optimum -> validHits.minByOrNull { optimum distSq it.key }?.let { closest -> val optimumRotation = eye.rotationTo(closest.key) return RotationRequest(optimumRotation, rotationConfig, closest.value, verify) } - } + }*/ return null } diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index b68bb2b76..e4daa5558 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -17,45 +17,43 @@ package com.lambda.module.modules.combat -import com.lambda.config.groups.InteractionSettings -import com.lambda.config.groups.RotationSettings import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext -import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.EntityEvent -import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent -import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.builders.ofBox -import com.lambda.graphics.renderer.esp.builders.ofShape import com.lambda.interaction.RotationManager.rotate +import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock -import com.lambda.interaction.visibilty.VisibilityChecker.lookAtEntity import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.threading.runSafe +import com.lambda.threading.runSafeGameScheduled import com.lambda.util.BlockUtils.blockState -import com.lambda.util.Communication.info +import com.lambda.util.SimpleTimer import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.combat.CombatUtils.explosionDamage import com.lambda.util.math.MathUtils.ceilToInt -import com.lambda.util.math.VecUtils.blockPos -import com.lambda.util.math.VecUtils.dist -import com.lambda.util.math.VecUtils.vec3d -import com.lambda.util.math.transform +import com.lambda.util.math.MathUtils.sq +import com.lambda.util.math.MathUtils.toInt +import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.math.VecUtils.flooredBlockPos +import com.lambda.util.math.VecUtils.getHitVec +import com.lambda.util.math.VecUtils.minus import com.lambda.util.world.fastEntitySearch +import com.lambda.util.world.raycast.RayCastMask import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.Blocks import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.EndCrystalEntity -import net.minecraft.item.Items +import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box +import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d -import java.awt.Color +import kotlin.concurrent.fixedRateTimer +import kotlin.math.ceil +import kotlin.math.max object CrystalAura : Module( name = "CrystalAura", @@ -65,362 +63,235 @@ object CrystalAura : Module( private val page by setting("Page", Page.General) /* General */ - private val strategy by setting("Strategy", Strategy.ExplodeBeforePlace) { page == Page.General } /* Targeting */ // ToDo: Targeting Range should be reach + crystal range (also based on min damage) private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting } /* Placing */ - private val place by setting("Place Crystals", true) { page == Page.Placing } - private val rotateToPlace by setting("Rotate To Place", true) { page == Page.Placing && place } - private val placementTimeout: Long by setting("Placement Timeout", 500L, 50L..2000L, 10L, "The timeout in milliseconds for placing a crystal", " ms") { page == Page.Placing && place }.apply { onValueSet { _, to -> pendingPlacements.setDecayTime(to) } } - private val maxPendingPlacements by setting("Max Pending Placements", 1, 1..20, 1, "The maximum number of pending placements to keep track of") { page == Page.Placing && place } - private val swap by setting("Swap", Hand.MAIN_HAND, "Automatically swap to place crystals") { page == Page.Placing && place } - private val placeMethod by setting("Place Sort", DamageSort.Deadly) { page == Page.Placing && place } - private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, "The minimum space between crystals", " blocks") { page == Page.Placing && place } - private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, "Delay between crystal placements", "ms") { page == Page.Placing && place } - private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..36.0, 0.5, "Minimum health to place a crystal") { page == Page.Placing && place } - private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to place a crystal") { page == Page.Placing && place } - private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, "Maximum self damage to place a crystal") { page == Page.Placing && place } - - /* Exploding */ - private val explode by setting("Explode Crystals", true) { page == Page.Exploding } - private val rotateToExplode by setting("Rotate To Explode", false) { page == Page.Exploding && explode } - private val explosionTimeout: Long by setting("Explosion Timeout", 500L, 50L..2000L, 10L, "The timeout in milliseconds for exploding a crystal", " ms") { page == Page.Exploding && explode }.apply { onValueSet { _, to -> pendingExplosions.setDecayTime(to) } } - private val maxPendingExplosions by setting("Max Pending Explosions", 1, 1..20, 1, "The maximum number of pending explosions to keep track of") { page == Page.Exploding && explode } - private val explosionDelay by setting("Explode Delay", 0, 0..20, 1, "Delay between crystal explosions", " ticks") { page == Page.Exploding && explode } - private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, "Range to explode crystals", " blocks") { page == Page.Exploding && explode } - private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) { page == Page.Exploding && explode } - private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to explode a crystal") { page == Page.Exploding && explode } + private val placeRange by setting("Place Range", 4.6, 1.0..7.0, 0.1, "Range to place crystals", " blocks") { page == Page.General } + private val explodeRange by setting("Explode Range", 3.0, 1.0..7.0, 0.1, "Range to explode crystals", " blocks") { page == Page.General } + private val placeDelay by setting("Place Delay", 50L, 0L..1000L, 5L, "Delay between placement attempts", " ms") { page == Page.General } + private val explodeDelay by setting("Explode Delay", 10L, 0L..1000L, 5L, "Delay between explosion attempts", " ms") { page == Page.General } + + private val minTargetDamage by setting("Min Target Damage", 6.0, 0.0..20.0, 0.5, "Minimum target damage to use crystals") { page == Page.General } + private val maxSelfDamage by setting("Max Self Damage", 8.0, 0.0..20.0, 0.5, "Maximum self damage to use crystals") { page == Page.General } + private val minHealth by setting("Min Health", 10.0, 0.0..36.0, 0.5, "Minimum player health to use crystals") { page == Page.General } + + private val oldPlace by setting("1.12 Placement", false) { page == Page.General } + private val crystalHeight get() = 1.0 + oldPlace.toInt() + + private val updateMode by setting("Update Mode", Update.Async) { page == Page.General } + private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L) { page == Page.General && updateMode == Update.Async } + private val updateDelay get() = if (updateMode == Update.Async) updateDelaySetting else 0L /* Rotation */ - private val rotation = RotationSettings(this) { page == Page.Rotation } + //private val rotateToPlace by setting("Rotate To Place", true) { page == Page.Rotation } + //private val rotateToExplode by setting("Rotate To Explode", true) { page == Page.Rotation } + //private val rotation = RotationSettings(this) { page == Page.Rotation } - /* Interaction */ - private val interact = InteractionSettings(this) { page == Page.Interaction } + //private val pendingPlacements = LimitedDecayQueue(Int.MAX_VALUE, 1000L) + //private val pendingExplosions = LimitedDecayQueue(Int.MAX_VALUE, 1000L) - /* Rendering */ - private val renderCrystals by setting("Render Crystal", true) { page == Page.Rendering } - private val crystalColor by setting("Color", ColorMode.TargetDamage) { page == Page.Rendering && renderCrystals } - private val crystalAlpha by setting("Color Alpha", 128, 0..255) { page == Page.Rendering && renderCrystals } - private val scalingFactor by setting("Scaling Factor", 1.0, 0.0..2.0, 0.1, "The scaling factor for the color mode") { page == Page.Rendering && renderCrystals } + private var rotationTarget: RotationRequest? = null - private enum class Page { - General, - Targeting, - Placing, - Exploding, - Rotation, - Interaction, - Rendering - } + // ToDo: crystal blueprint + private val damage = mutableMapOf() + private val updateTimer = SimpleTimer() - private val target: LivingEntity? get() = targeting.target() + private val placeTimer = SimpleTimer() + private val explodeTimer = SimpleTimer() - private val pendingPlacements = LimitedDecayQueue(64, placementTimeout) - private val possiblePlaceOpportunities = mutableListOf() + private const val EXPLOSION_STRENGTH = 6.0 - private val pendingExplosions = LimitedDecayQueue(64, explosionTimeout) - private val possibleExplodeOpportunities = mutableListOf() + private enum class Page { + General, + Targeting + } - private var explosionRequest: ExplosionOpportunity? = null - private var placementRequest: PlacementOpportunity? = null - private var rotationRequest: RotationRequest? = null + private enum class Update { + Async, + Ticked + } init { - listen { - explodeCrystal() - placeCrystal() - determineActionForNextTick() + fixedRateTimer( + name = "Crystal Aura Thread", + daemon = true, + initialDelay = 0L, + period = 3L + ) { + if (CrystalAura.isDisabled || updateMode != Update.Async) return@fixedRateTimer + + runSafeGameScheduled { + tick() + } } - rotate { request { rotationRequest } } - - listen { event -> - pendingExplosions.removeAll { it.crystal.id == event.entity.id } + listen { + if (updateMode == Update.Ticked) tick() } - listen { event -> - pendingPlacements.removeAll { it.blockPos.up() == event.entity.blockPos} + rotate { + request { rotationTarget } } - listen { - possibleExplodeOpportunities.forEach { opportunity -> - val color = opportunity.color - it.renderer.ofBox(opportunity.crystal.boundingBox, color, color) - } + /*listen { event -> + val crystal = (event.entity as? EndCrystalEntity?) ?: return@listen + pendingPlacements.remove(crystal.baseBlockPos) + }*/ - possiblePlaceOpportunities.forEach { opportunity -> - val color = opportunity.color - it.renderer.ofShape(opportunity.blockPos, color, color) - } + /*listen { event -> + val crystal = (event.entity as? EndCrystalEntity?) ?: return@listen + pendingExplosions.remove(crystal.baseBlockPos) + }*/ - possibleExplodeOpportunities.clear() - possiblePlaceOpportunities.clear() - } - - onDisable { clear() } - listen { clear() } + /*listen { event -> + val packet = (event.packet as? PlaySoundS2CPacket) ?: return@listen + }*/ } - private fun clear() { - pendingPlacements.clear() - possiblePlaceOpportunities.clear() - pendingExplosions.clear() - possibleExplodeOpportunities.clear() + private fun SafeContext.tick() { + updateDamageMap() - placementRequest = null - explosionRequest = null - rotationRequest = null + tickExplosion() + tickPlacement() } - private fun SafeContext.determineActionForNextTick() { - target?.let { tar -> - collectExplosions(tar) - collectPlacements(tar) + private fun SafeContext.updateDamageMap() { + val target = targeting.target() ?: run { + damage.clear() + return + } - val bestPlacement = possiblePlaceOpportunities.firstNotNullOfOrNull { opportunity -> - lookAtBlock(opportunity.blockPos, rotation, interact) to opportunity - } + updateTimer.runIfPassed(updateDelay) { + damage.clear() - val bestExplosion = possibleExplodeOpportunities.firstNotNullOfOrNull { opportunity -> - lookAtEntity(opportunity.crystal, rotation, interact) to opportunity - } + val range = ceil(max(placeRange, explodeRange) + 1) + val rangeInt = range.toInt() - if (explode - && pendingExplosions.size <= maxPendingExplosions - && bestExplosion != null - && (bestPlacement == null || bestExplosion.second.isBest(bestPlacement.second)) - ) { - if (rotateToExplode) rotationRequest = bestExplosion.first - explosionRequest = bestExplosion.second - return + // Iterate through possible place positions and calculate damage information for each + BlockPos.iterateOutwards(player.blockPos.up(), rangeInt, rangeInt, rangeInt).forEach { pos -> + mapPlaceDamage(pos, target, rangeInt) } - if (place - && pendingPlacements.size <= maxPendingPlacements - && bestPlacement != null - && (bestExplosion == null || bestPlacement.second.isBest(bestExplosion.second)) - ) { - if (rotateToPlace) rotationRequest = bestPlacement.first - placementRequest = bestPlacement.second - return - } + // Same for crystals, that could not be placed here, but exist (e.g. The base block was broken) + val mutableBlockPos = BlockPos.Mutable() + fastEntitySearch(range).forEach { + mutableBlockPos.set(it.x, it.y - 0.5, it.z) - placementRequest = null - explosionRequest = null - rotationRequest = null + if (damage[mutableBlockPos] == null) { + mapDamage(mutableBlockPos, target) + } + } } } - private fun ExplosionOpportunity.isBest(placement: PlacementOpportunity) = - targetDamage >= placement.targetDamage && pendingExplosions.none { it.targetDamage >= targetDamage } + private fun SafeContext.tickExplosion() = + explodeTimer.runIfPassed(explodeDelay) { + val crystal = fastEntitySearch( + explodeRange + 1 + ).mapNotNull { crystal -> + val damage = damage[crystal] ?: return@mapNotNull null - private fun PlacementOpportunity.isBest(explosion: ExplosionOpportunity) = - targetDamage >= explosion.targetDamage && pendingPlacements.none { it.targetDamage >= targetDamage } + crystal to damage + }.maxByOrNull { + it.second.target + }?.first ?: return@runIfPassed - private fun SafeContext.placeCrystal() { - getNextPlacement { request, placement -> - if (!place || (!request.isValid && rotateToPlace)) return@getNextPlacement - - val inMainHand = player.mainHandStack.item == Items.END_CRYSTAL - val inOffHand = player.offHandStack.item == Items.END_CRYSTAL - if (!inMainHand && !inOffHand) return@getNextPlacement - - val hand = if (inMainHand) Hand.MAIN_HAND else Hand.OFF_HAND + explodeInternal(crystal.id) + } - val hitResult = request.cast?.blockResult ?: return@getNextPlacement - val actionResult = interaction.interactBlock(player, hand, hitResult) - if (!actionResult.isAccepted) return@getNextPlacement + private fun SafeContext.explodeInternal(id: Int) { + connection.sendPacket( + PlayerInteractEntityC2SPacket( + id, player.isSneaking, PlayerInteractEntityC2SPacket.ATTACK + ) + ) - if (actionResult.shouldSwingHand() && interact.swingHand) { - player.swingHand(hand) - } - -// info("Placed at ${placement.blockPos.toShortString()} with target damage ${placement.targetDamage} and self damage ${placement.selfDamage} at ${placement.distanceToTarget}m") - pendingPlacements.add(placement) - placementRequest = null - rotationRequest = null - } + player.swingHand(Hand.MAIN_HAND) } - private fun SafeContext.explodeCrystal() { - explosionRequest?.let { opportunity -> - if (!explode || (rotationRequest?.isValid == false && rotateToExplode)) return + private fun SafeContext.tickPlacement() = + placeTimer.runIfPassed(placeDelay) { + val placeBlock = damage.values + .maxByOrNull { it.target }?.blockPos ?: return@runIfPassed - interaction.attackEntity(player, opportunity.crystal) - if (interact.swingHand) { - player.swingHand(Hand.MAIN_HAND) - } - -// info("Exploded ${opportunity.crystal.name.string} at ${opportunity.crystal.pos} with target damage ${opportunity.targetDamage} and self damage ${opportunity.selfDamage} at ${opportunity.distanceToTarget}m") - pendingExplosions.add(opportunity) - explosionRequest = null - rotationRequest = null + placeInternal(placeBlock, Hand.MAIN_HAND) } - } - private fun SafeContext.findExplosionOpportunities(target: LivingEntity): List { - val maximumRange = (1 - (explodeMinDamage / 12.0)) * 12.0 - return fastEntitySearch(maximumRange, target.blockPos) - .map { crystal -> - val targetDamage = crystalDamage(crystal.pos, target) - val selfDamage = crystalDamage(crystal.pos, player) - val distance = target.dist(crystal.pos) - ExplosionOpportunity(crystal, targetDamage, selfDamage, distance) - }.filterNot { it in pendingExplosions }.sortedWith(explodeMethod.comparator) - } + private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand) { + val angles = player.eyePos.rotationTo(blockPos.crystalPosition) + val cast = angles.rayCast(placeRange, mask = RayCastMask.BLOCK)?.blockResult ?: return - private fun SafeContext.findPlacementOpportunities(target: LivingEntity): List { - // This formula is derived from the explosion damage scaling logic. The damage decreases linearly - // with distance, modeled as `damage = (1 - (distance / (power * 2))) * exposure`, where power is the - // explosion's strength (6.0) and exposure defines how much of the explosion affects the target. - val maximumRange = ((1 - (placeMinDamage / 12.0)) * 12.0).ceilToInt() + val actionResult = interaction.interactBlock(player, hand, cast) + if (!actionResult.isAccepted || !actionResult.shouldSwingHand()) return - return BlockPos.iterateOutwards(target.blockPos, maximumRange, maximumRange, maximumRange).mapNotNull { pos -> - targetData(pos, target) - }.filterNot { it in pendingPlacements }.sortedWith(placeMethod.comparator) + player.swingHand(hand) } - private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): PlacementOpportunity? { - val inRange = pos.dist(player.eyePos) < interact.reach + 1 - if (!inRange) return null + private fun SafeContext.mapPlaceDamage(pos: BlockPos, target: LivingEntity, range: Int) { + if (pos distSq player.eyePos > range.sq) return + // Check if crystals could be placed on the base block val state = pos.blockState(world) val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) - if (!isOfBlock) return null - - if (pendingPlacements.any { it.blockPos == pos }) return null + if (!isOfBlock) return + // Check if the block above is air and other conditions for valid crystal placement val above = pos.up() - val isAirAbove = world.isAir(above) - if (!isAirAbove) return null + if (!world.isAir(above)) return + if (oldPlace && !world.isAir(above.up())) return - val checkBox = Box(above).withMaxY(above.y + 2.0) + // Exclude blocks blocked by entities + val checkBox = Box(above).withMaxY(above.y + crystalHeight) val entitiesAbove = world.getOtherEntities(null, checkBox) - if (entitiesAbove.isNotEmpty()) return null + if (entitiesAbove.any { it !is EndCrystalEntity }) return - val crystalPos = above.vec3d.add(0.5, 0.0, 0.5) - val targetDamage = explosionDamage(crystalPos, target, 6.0) - if (targetDamage <= placeMinDamage) return null - - val selfDamage = explosionDamage(crystalPos, player, 6.0) - if (selfDamage > placeMaxSelfDamage) return null - val distance = target.dist(crystalPos) - - return PlacementOpportunity(pos.toImmutable(), targetDamage, selfDamage, distance) + mapDamage(pos, target) } - private fun SafeContext.collectExplosions(target: LivingEntity) { - val targets = findExplosionOpportunities(target) - possibleExplodeOpportunities.addAll(targets) - } + private fun SafeContext.mapDamage(pos: BlockPos, target: LivingEntity) { + val crystalPos = pos.crystalPosition - private fun SafeContext.collectPlacements(target: LivingEntity) { - val targets = findPlacementOpportunities(target) - possiblePlaceOpportunities.addAll(targets) - } - private fun getNextPlacement(block: (RotationRequest, PlacementOpportunity) -> Unit) = - rotationRequest?.let { rot -> placementRequest?.let { explode -> block(rot, explode) }} + // Calculate the damage to the target from the explosion of the crystal + val targetDamage = explosionDamage(crystalPos, target, EXPLOSION_STRENGTH) + if (targetDamage < minTargetDamage) return - /** - * @property blockPos The block position associated with this placement target. - * @property targetDamage The damage inflicted on the target. - * @property selfDamage The damage inflicted on the self due to some actions or interactions. - * @property distanceToTarget The distance to the target from the player or reference point. - */ - data class PlacementOpportunity( - val blockPos: BlockPos, - override val targetDamage: Double, - override val selfDamage: Double, - override val distanceToTarget: Double, - ) : Opportunity() + // Calculate the self-damage for the player + val selfDamage = explosionDamage(crystalPos, player, EXPLOSION_STRENGTH) + if (selfDamage > maxSelfDamage) return - /** - * @property crystal The targeted `EndCrystalEntity` in the context of the explosion. - * @property targetDamage The amount of damage inflicted on the target as part of the explosion. - * @property selfDamage The amount of damage inflicted on the self due to the explosion. - * @property distanceToTarget The distance from the origin point (e.g., player) to the target. - */ - data class ExplosionOpportunity( - val crystal: EndCrystalEntity, - override val targetDamage: Double, - override val selfDamage: Double, - override val distanceToTarget: Double - ) : Opportunity() + val immutablePos = pos.toImmutable() - /** - * Represents an opportunity to cause damage. - * - * @property targetDamage The damage inflicted on the target. - * @property selfDamage The damage inflicted on the self due to some actions or interactions. - * @property distanceToTarget The distance to the target from the player or reference point. - * @property color The dynamically calculated color based on the color mode and associated parameters. - */ - abstract class Opportunity { - abstract val targetDamage: Double - abstract val selfDamage: Double - abstract val distanceToTarget: Double - - val color: Color by lazy { - when (crystalColor) { - ColorMode.TargetDamage -> { - val targetHealth = target?.health?.toDouble()?.takeIf { it > 0.0 } ?: 20.0 - val normalizedDamage = (targetDamage / targetHealth).coerceIn(0.0, 1.0) - val adjustedRed = (normalizedDamage * 255.0).toInt().coerceIn(0, 255) - val adjustedGreen = (255 - adjustedRed).coerceIn(0, 255) - Color(adjustedRed, adjustedGreen, 0, crystalAlpha) - } - ColorMode.SelfDamage -> { - runSafe { - val selfHealth = player.health.toDouble() - val damage = selfDamage.coerceAtMost(selfHealth) - val red = transform(damage, 0.0, selfHealth, 0.0, 255.0).toInt() - Color(red, 255 - red, 0, crystalAlpha) - } ?: Color.WHITE - } - ColorMode.Distance -> { - val distance = distanceToTarget.coerceAtMost(explodeRange) - val red = transform(distance, 0.0, explodeRange, 0.0, 255.0).toInt() - Color(red, 0, 255 - red, crystalAlpha) - } - } - } + // Return the calculated damage info if conditions are met + damage[immutablePos] = DamageInfo( + immutablePos, + targetDamage, + selfDamage + ) } - /** - * Damage sorter parameter - * - * deadly -> always prioritize enemy damage no matter what - * balanced -> sort by the highest ratio of enemy damage to self-damage - * safe -> always prioritize the least amount of self-damage - */ - private enum class DamageSort(val comparator: Comparator) { - Deadly(compareByDescending { it.targetDamage }.thenBy { it.selfDamage }), - Balanced(compareByDescending { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }), - Safe(compareBy { it.selfDamage }.thenByDescending { it.targetDamage }); - } + private val EndCrystalEntity.baseBlockPos get() = + (pos - Vec3d(0.0, 0.5, 0.0)).flooredBlockPos - private enum class Strategy { - ExplodeBeforePlace, - PlaceBeforeExplode - } + private val BlockPos.crystalPosition get() = + this.getHitVec(Direction.UP) + + private operator fun MutableMap.get(entity: EndCrystalEntity) = + get(entity.baseBlockPos) /** - * Represents the different modes of color categorization or operations. + * Represents the damage information resulting from placing an end crystal on a given [blockPos] + * and causing an explosion that targets current target entity. * - * Enum values: - * - TargetDamage: Indicates color mode based on damage to a target. - * - SelfDamage: Indicates color mode based on damage to oneself. - * - Distance: Indicates color mode based on distance criteria. + * @property blockPos The position of the base block where the crystal is placed. + * @property target The amount of damage inflicted on the target. + * @property self The amount of damage inflicted on the player. */ - private enum class ColorMode { - TargetDamage, - SelfDamage, - Distance - } - - private fun SafeContext.crystalDamage(vec3d: Vec3d, target: LivingEntity) = - explosionDamage(vec3d, target, 6.0) + private open class DamageInfo( + val blockPos: BlockPos, + val target: Double, + val self: Double, + ) } diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index bb329a6be..50a06eb2a 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -126,7 +126,7 @@ inline fun runGameScheduled(crossinline block: () -> Unit) { * * @param block The task to be executed on the game's main thread within a safe context. */ -inline fun runSafeGameConcurrent(crossinline block: SafeContext.() -> Unit) { +inline fun runSafeGameScheduled(crossinline block: SafeContext.() -> Unit) { runGameScheduled { runSafe { block() } } } diff --git a/common/src/main/kotlin/com/lambda/util/Communication.kt b/common/src/main/kotlin/com/lambda/util/Communication.kt index 36804a262..687dd42cd 100644 --- a/common/src/main/kotlin/com/lambda/util/Communication.kt +++ b/common/src/main/kotlin/com/lambda/util/Communication.kt @@ -28,7 +28,7 @@ import com.lambda.module.Module import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.GuiSettings import com.lambda.threading.runSafe -import com.lambda.threading.runSafeGameConcurrent +import com.lambda.threading.runSafeGameScheduled import com.lambda.util.StringUtils.capitalize import com.lambda.util.text.* import net.minecraft.client.toast.SystemToast @@ -74,7 +74,7 @@ object Communication { buildText { text(this@toast.source(logLevel, color = Color.YELLOW)) }.let { title -> - runSafeGameConcurrent { + runSafeGameScheduled { mc.toastManager.add(logLevel.toast(title, message)) } } @@ -99,7 +99,7 @@ object Communication { text(this@log.source(logLevel, source, textSource)) text(message) }.let { log -> - runSafeGameConcurrent { + runSafeGameScheduled { player.sendMessage(log) } } diff --git a/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt b/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt index f2e48be91..6ec7a62cf 100644 --- a/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt @@ -25,9 +25,12 @@ import kotlin.math.roundToInt import kotlin.math.sqrt object VecUtils { - val Vec3d.blockPos: BlockPos + val Vec3d.roundedBlockPos: BlockPos get() = BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt()) + val Vec3d.flooredBlockPos: BlockPos + get() = BlockPos.ofFloored(this) + val Vec3i.vec3d get() = Vec3d.of(this) fun BlockPos.getHitVec(side: Direction) = diff --git a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt index e4dfa61c2..efea38a56 100644 --- a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt @@ -40,12 +40,12 @@ object RayCastUtils { fun SafeContext.rayCast( start: Vec3d, - rotation: Vec3d, + direction: Vec3d, reach: Double, mask: RayCastMask, fluids: Boolean = false, ): HitResult? { - val vec = rotation.multiply(reach) + val vec = direction.multiply(reach) val point = start.add(vec) val block = run { diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index e916c247e..db3df6d09 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -63,6 +63,8 @@ accessible field net/minecraft/network/ClientConnection packetsSentCounter I accessible field net/minecraft/network/ClientConnection packetsReceivedCounter I accessible field net/minecraft/network/packet/c2s/login/LoginKeyC2SPacket encryptedSecretKey [B accessible field net/minecraft/network/packet/c2s/login/LoginKeyC2SPacket nonce [B +accessible method net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket (IZLnet/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler;)V +accessible field net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket ATTACK Lnet/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler; # Other accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; From d5c51e468343f47a9c539abefd18e8a9caed397f Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 17 Jan 2025 18:51:11 -0500 Subject: [PATCH 35/69] idk what happened --- .../com/lambda/interaction/construction/blueprint/Blueprint.kt | 2 +- common/src/main/kotlin/com/lambda/util/math/Vectors.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt index d0a108e28..693b3c35e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt @@ -21,7 +21,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.BlockUtils.blockPos import com.lambda.util.collections.ResettableLazy import com.lambda.util.extension.Structure -import com.lambda.util.math.VecUtils.roundedBlockPos +import com.lambda.util.math.roundedBlockPos import net.minecraft.structure.StructureTemplate import net.minecraft.util.math.* diff --git a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt index 823b54328..370de99cc 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt @@ -38,7 +38,7 @@ operator fun Vec2f.component2() = y fun Vec3d.approximate(other: Vec3d, precision: Double = 2.0E-4): Boolean = (subtract(other) distSq Vec3d.ZERO) > precision.pow(2) -val Vec3d.blockPos: BlockPos +val Vec3d.roundedBlockPos: BlockPos get() = BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt()) fun Vec3d.interpolate(value: Double, max: Vec3d) = lerp(value, this, max) From a9ab201fe381d04508e07bde33c0591f8755bd93 Mon Sep 17 00:00:00 2001 From: blade Date: Sun, 19 Jan 2025 16:43:16 +0300 Subject: [PATCH 36/69] Lightfast CrystalAura --- .../container/containers/StashContainer.kt | 2 +- .../com/lambda/module/hud/Coordinates.kt | 4 +- .../module/modules/combat/CrystalAura.kt | 535 +++++++++++++----- .../com/lambda/util/combat/CombatUtils.kt | 10 +- .../kotlin/com/lambda/util/math/Vectors.kt | 35 +- gradle.properties | 2 +- 6 files changed, 412 insertions(+), 176 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt index a82d27699..763d8ac52 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt @@ -19,7 +19,7 @@ package com.lambda.interaction.material.container.containers import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.math.VecUtils.roundedBlockPos +import com.lambda.util.math.roundedBlockPos import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal diff --git a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt index 5f47d28b5..c966b61bf 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt @@ -24,8 +24,8 @@ import com.lambda.threading.runSafe import com.lambda.util.Formatting.asString import com.lambda.util.Formatting.string import com.lambda.util.extension.dimensionName -import com.lambda.util.math.VecUtils.netherCoord -import com.lambda.util.math.VecUtils.overworldCoord +import com.lambda.util.math.netherCoord +import com.lambda.util.math.overworldCoord import net.minecraft.registry.RegistryKey import net.minecraft.world.World diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index e4daa5558..29a5a5756 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -20,39 +20,39 @@ package com.lambda.module.modules.combat import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext import com.lambda.event.events.EntityEvent +import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.gl.Matrices +import com.lambda.graphics.gl.Matrices.buildWorldProjection +import com.lambda.graphics.gl.Matrices.withVertexTransform +import com.lambda.graphics.renderer.gui.font.FontRenderer +import com.lambda.graphics.renderer.gui.font.LambdaEmoji +import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.interaction.RotationManager.rotate import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runSafe import com.lambda.threading.runSafeGameScheduled import com.lambda.util.BlockUtils.blockState import com.lambda.util.SimpleTimer -import com.lambda.util.collections.LimitedDecayQueue -import com.lambda.util.combat.CombatUtils.explosionDamage +import com.lambda.util.combat.CombatUtils.crystalDamage +import com.lambda.util.math.* import com.lambda.util.math.MathUtils.ceilToInt -import com.lambda.util.math.MathUtils.sq -import com.lambda.util.math.MathUtils.toInt -import com.lambda.util.math.VecUtils.distSq -import com.lambda.util.math.VecUtils.flooredBlockPos -import com.lambda.util.math.VecUtils.getHitVec -import com.lambda.util.math.VecUtils.minus +import com.lambda.util.math.MathUtils.roundToStep import com.lambda.util.world.fastEntitySearch import com.lambda.util.world.raycast.RayCastMask import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.Blocks +import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.EndCrystalEntity import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket import net.minecraft.util.Hand -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Box -import net.minecraft.util.math.Direction -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.* import kotlin.concurrent.fixedRateTimer -import kotlin.math.ceil import kotlin.math.max object CrystalAura : Module( @@ -63,213 +63,402 @@ object CrystalAura : Module( private val page by setting("Page", Page.General) /* General */ - - /* Targeting */ - // ToDo: Targeting Range should be reach + crystal range (also based on min damage) - private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting } - - /* Placing */ private val placeRange by setting("Place Range", 4.6, 1.0..7.0, 0.1, "Range to place crystals", " blocks") { page == Page.General } private val explodeRange by setting("Explode Range", 3.0, 1.0..7.0, 0.1, "Range to explode crystals", " blocks") { page == Page.General } private val placeDelay by setting("Place Delay", 50L, 0L..1000L, 5L, "Delay between placement attempts", " ms") { page == Page.General } private val explodeDelay by setting("Explode Delay", 10L, 0L..1000L, 5L, "Delay between explosion attempts", " ms") { page == Page.General } + private val updateMode by setting("Update Mode", UpdateMode.Async) { page == Page.General } + private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L) { page == Page.General && updateMode == UpdateMode.Async } + private val updateDelay get() = if (updateMode == UpdateMode.Async) updateDelaySetting else 0L + private val debug by setting("Debug", false) { page == Page.General } + + /* Placement */ + private val priorityMode by setting("Crystal Priority", Priority.Damage) { page == Page.Placement } + private val minDamageAdvantage by setting("Min Damage Advantage", 4.0, 1.0..10.0, 0.5) { page == Page.Placement && priorityMode == Priority.Advantage } + private val minTargetDamage by setting("Min Target Damage", 6.0, 0.0..20.0, 0.5, "Minimum target damage to use crystals") { page == Page.Placement } + private val maxSelfDamage by setting("Max Self Damage", 8.0, 0.0..36.0, 0.5, "Maximum self damage to use crystals") { page == Page.Placement } + //private val minHealth by setting("Min Health", 10.0, 0.0..36.0, 0.5, "Minimum player health to use crystals") { page == Page.General } + private val oldPlace by setting("1.12 Placement", false) { page == Page.Placement } + + /* Prediction */ + private val prediction by setting("Prediction", PredictionMode.None) { page == Page.Prediction } + private val predictionPackets by setting("Prediction Packets", 1, 1..20) { page == Page.Prediction && prediction.isActive } + private val postPlace by setting("Post Place", false) { page == Page.Prediction && prediction.isActive && predictionPackets > 1 } + private val packetLifetime by setting("Packet Lifetime", 300L, 50L..1000L) { page == Page.Prediction && prediction.onPlace } - private val minTargetDamage by setting("Min Target Damage", 6.0, 0.0..20.0, 0.5, "Minimum target damage to use crystals") { page == Page.General } - private val maxSelfDamage by setting("Max Self Damage", 8.0, 0.0..20.0, 0.5, "Maximum self damage to use crystals") { page == Page.General } - private val minHealth by setting("Min Health", 10.0, 0.0..36.0, 0.5, "Minimum player health to use crystals") { page == Page.General } - - private val oldPlace by setting("1.12 Placement", false) { page == Page.General } - private val crystalHeight get() = 1.0 + oldPlace.toInt() - - private val updateMode by setting("Update Mode", Update.Async) { page == Page.General } - private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L) { page == Page.General && updateMode == Update.Async } - private val updateDelay get() = if (updateMode == Update.Async) updateDelaySetting else 0L + /* Targeting */ + private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting } /* Rotation */ //private val rotateToPlace by setting("Rotate To Place", true) { page == Page.Rotation } //private val rotateToExplode by setting("Rotate To Explode", true) { page == Page.Rotation } //private val rotation = RotationSettings(this) { page == Page.Rotation } + private var rotationTarget: RotationRequest? = null - //private val pendingPlacements = LimitedDecayQueue(Int.MAX_VALUE, 1000L) - //private val pendingExplosions = LimitedDecayQueue(Int.MAX_VALUE, 1000L) + private val blueprint = mutableMapOf() - private var rotationTarget: RotationRequest? = null + private val damage = mutableListOf() + private val actionMap = mutableMapOf>() + private var actionType = ActionType.Normal - // ToDo: crystal blueprint - private val damage = mutableMapOf() private val updateTimer = SimpleTimer() - private val placeTimer = SimpleTimer() private val explodeTimer = SimpleTimer() - private const val EXPLOSION_STRENGTH = 6.0 + // ToDo: pending prediction decay queue (crystalId s) + private val predictionTimer = SimpleTimer() + private var lastEntityId = 0 - private enum class Page { - General, - Targeting + private val collidingOffsets = mutableListOf().apply { + for (x in -1..1) { + for (z in -1..1) { + for (y in 0..1) { + if (x != 0 && y != 0 && z != 0) add(BlockPos(x, y, z)) + } + } + } } - private enum class Update { - Async, - Ticked - } + private val font = FontRenderer(LambdaFont.FiraSansRegular, LambdaEmoji.Twemoji) init { + // Async ticking fixedRateTimer( name = "Crystal Aura Thread", daemon = true, initialDelay = 0L, period = 3L ) { - if (CrystalAura.isDisabled || updateMode != Update.Async) return@fixedRateTimer + if (CrystalAura.isDisabled || updateMode != UpdateMode.Async) return@fixedRateTimer runSafeGameScheduled { tick() } } + // Ticking with alignment listen { - if (updateMode == Update.Ticked) tick() + if (updateMode == UpdateMode.Ticked) tick() } - rotate { - request { rotationTarget } + listen { + if (!debug) return@listen + + // Build the buffer + blueprint.values.forEach { + it.buildDebug() + } + + // Draw the font + Matrices.push { + val c = mc.gameRenderer.camera.pos.negate() + translate(c.x, c.y, c.z) + font.render() + } } - /*listen { event -> - val crystal = (event.entity as? EndCrystalEntity?) ?: return@listen - pendingPlacements.remove(crystal.baseBlockPos) - }*/ + listen { event -> + lastEntityId = event.entity.id + predictionTimer.reset() - /*listen { event -> - val crystal = (event.entity as? EndCrystalEntity?) ?: return@listen - pendingExplosions.remove(crystal.baseBlockPos) - }*/ + val crystal = event.entity as? EndCrystalEntity ?: return@listen + val pos = crystal.baseBlockPos - /*listen { event -> - val packet = (event.packet as? PlaySoundS2CPacket) ?: return@listen - }*/ - } + val opportunity = blueprint[pos] ?: return@listen + opportunity.crystal = crystal - private fun SafeContext.tick() { - updateDamageMap() + if (!prediction.onPacket) return@listen + if (getBestOpportunity() != opportunity) return@listen + + repeat(predictionPackets) { + val offset = if (postPlace) 0 else it + explodeInternal(lastEntityId + offset) + + if (postPlace) { + placeInternal(pos, Hand.MAIN_HAND) + lastEntityId++ + placeTimer.reset() + } + } + } + + listen { event -> + val crystal = event.entity as? EndCrystalEntity ?: return@listen + val pos = crystal.baseBlockPos - tickExplosion() - tickPlacement() + blueprint[pos]?.crystal = null + //pendingExplosions.remove(blockPos) + } + + onEnable { + lastEntityId = -1 + } + + rotate { + request { rotationTarget } + } } - private fun SafeContext.updateDamageMap() { + private fun SafeContext.tick() { val target = targeting.target() ?: run { - damage.clear() + blueprint.clear() return } - updateTimer.runIfPassed(updateDelay) { - damage.clear() + // Update the blueprint + buildBlueprint(target) - val range = ceil(max(placeRange, explodeRange) + 1) - val rangeInt = range.toInt() + // Choosing and running the best opportunity + val best = getBestOpportunity() ?: return - // Iterate through possible place positions and calculate damage information for each - BlockPos.iterateOutwards(player.blockPos.up(), rangeInt, rangeInt, rangeInt).forEach { pos -> - mapPlaceDamage(pos, target, rangeInt) - } + if (!best.blocked) { + best.explode() + best.place() + return + } - // Same for crystals, that could not be placed here, but exist (e.g. The base block was broken) - val mutableBlockPos = BlockPos.Mutable() - fastEntitySearch(range).forEach { - mutableBlockPos.set(it.x, it.y - 0.5, it.z) + val mutableBlockPos = BlockPos.Mutable() - if (damage[mutableBlockPos] == null) { - mapDamage(mutableBlockPos, target) - } - } - } - } + // Break crystals nearby if the best crystal placement is blocked by other crystals + collidingOffsets.mapNotNull { + mutableBlockPos.set( + best.blockPos.x + it.x, + best.blockPos.y + it.y, + best.blockPos.z + it.z + ) - private fun SafeContext.tickExplosion() = - explodeTimer.runIfPassed(explodeDelay) { - val crystal = fastEntitySearch( - explodeRange + 1 - ).mapNotNull { crystal -> - val damage = damage[crystal] ?: return@mapNotNull null + blueprint[mutableBlockPos] + }.filter { it.hasCrystal }.maxByOrNull { it.priority }?.explode() - crystal to damage - }.maxByOrNull { - it.second.target - }?.first ?: return@runIfPassed + best.place() + } - explodeInternal(crystal.id) + private fun SafeContext.buildBlueprint(target: LivingEntity) = updateTimer.runIfPassed(updateDelay) { + blueprint.clear() + damage.clear() + actionMap.clear() + actionType = ActionType.Normal + + // Build damage info + fun info( + pos: BlockPos, target: LivingEntity, + blocked: Boolean, + crystal: EndCrystalEntity? = null + ): Opportunity? { + val crystalPos = pos.crystalPosition + + // Calculate the damage to the target from the explosion of the crystal + val targetDamage = crystalDamage(crystalPos, target) + if (targetDamage < minTargetDamage) return null + + // Calculate the self-damage for the player + val selfDamage = crystalDamage(crystalPos, player) + if (selfDamage > maxSelfDamage) return null + + if (priorityMode == Priority.Advantage && priorityMode.factor(targetDamage, selfDamage) < minDamageAdvantage) return null + + // Return the calculated damage info if conditions are met + return Opportunity( + pos.toImmutable(), + targetDamage, + selfDamage, + blocked, + crystal + ) } - private fun SafeContext.explodeInternal(id: Int) { - connection.sendPacket( - PlayerInteractEntityC2SPacket( - id, player.isSneaking, PlayerInteractEntityC2SPacket.ATTACK + // Extra checks for placement, because you may explode in special cases(crystal in the air) but not place + @Suppress("ConvertArgumentToSet") + fun placeInfo( + pos: BlockPos, + target: LivingEntity + ): Opportunity? { + // Check if crystals could be placed on the base block + val state = pos.blockState(world) + val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) + if (!isOfBlock) return null + + // Check if the block above is air and other conditions for valid crystal placement + val above = pos.up() + if (!world.isAir(above)) return null + if (oldPlace && !world.isAir(above.up())) return null + + // Exclude blocks blocked by entities + val crystalBox = pos.crystalBox + + val entitiesNearby = fastEntitySearch(3.5, pos) + val crystals = entitiesNearby.filterIsInstance() as MutableList + val otherEntities = entitiesNearby - crystals + player + + if (otherEntities.any { + it.boundingBox.intersects(crystalBox) + }) return null + + // Placement collision checks + val baseCrystal = crystals.firstOrNull { + it.baseBlockPos == pos + } + + val crystalPlaceBox = pos.crystalPlaceHitbox + val blocked = baseCrystal == null && crystals.any { + it.boundingBox.intersects(crystalPlaceBox) + } + + return info( + pos, + target, + blocked, + baseCrystal ) - ) + } - player.swingHand(Hand.MAIN_HAND) - } + val range = max(placeRange, explodeRange) + 1 + val rangeInt = range.ceilToInt() - private fun SafeContext.tickPlacement() = - placeTimer.runIfPassed(placeDelay) { - val placeBlock = damage.values - .maxByOrNull { it.target }?.blockPos ?: return@runIfPassed + // Iterate through existing crystals + val crystalBase = BlockPos.Mutable() + fastEntitySearch(range).forEach { crystal -> + crystalBase.set(crystal.x, crystal.y - 0.5, crystal.z) + damage += info(crystalBase, target, false, crystal) ?: return@forEach + } + + // Iterate through possible place positions and calculate damage information for each + BlockPos.iterateOutwards(player.blockPos.up(), rangeInt, rangeInt, rangeInt).forEach { pos -> + if (pos distSq player.pos > range * range) return@forEach + if (damage.any { info -> info.blockPos == pos }) return@forEach - placeInternal(placeBlock, Hand.MAIN_HAND) + damage += placeInfo(pos, target) ?: return@forEach } - private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand) { - val angles = player.eyePos.rotationTo(blockPos.crystalPosition) - val cast = angles.rayCast(placeRange, mask = RayCastMask.BLOCK)?.blockResult ?: return + // Map opportunities + damage.forEach { + blueprint[it.blockPos] = it + } - val actionResult = interaction.interactBlock(player, hand, cast) - if (!actionResult.isAccepted || !actionResult.shouldSwingHand()) return + // ToDo: force place - player.swingHand(hand) + // Associate by actions + blueprint.values.forEach { opportunity -> + actionMap.getOrPut(opportunity.actionType, ::mutableListOf) += opportunity + + if (opportunity.actionType.priority > actionType.priority) { + actionType = opportunity.actionType + } + } } - private fun SafeContext.mapPlaceDamage(pos: BlockPos, target: LivingEntity, range: Int) { - if (pos distSq player.eyePos > range.sq) return + /** + * Represents the damage information resulting from placing an end crystal on a given [blockPos] + * and causing an explosion that targets current target entity. + * + * @property blockPos The position of the base block where the crystal is placed. + * @property target The amount of damage inflicted on the target. + * @property self The amount of damage inflicted on the player. + * @property blocked Whether the placement on [blockPos] is blocked by other crystals. + * @property crystal A crystal that is placed on [blockPos]. + */ + private class Opportunity( + val blockPos: BlockPos, + val target: Double, + val self: Double, + var blocked: Boolean, + var crystal: EndCrystalEntity? // ToDo: packet-based update wisely + ) { + var actionType = ActionType.Normal + val priority = priorityMode.factor(target, self) + val hasCrystal = crystal != null + + /** + * Places the crystal on [blockPos] + * @return Whether the delay passed, null if the interaction failed + */ + fun place(): Boolean? { + val timePassed = placeTimer.timePassed(placeDelay) + if (!timePassed) return false + + runSafe { + placeInternal(blockPos, Hand.MAIN_HAND) + + run { + if (!prediction.onPlace || predictionTimer.timePassed(packetLifetime)) return@run + + repeat(predictionPackets) { + if (it != 0) placeInternal(blockPos, Hand.MAIN_HAND) + explodeInternal(++lastEntityId) + } + } + + placeTimer.reset() + } ?: return null - // Check if crystals could be placed on the base block - val state = pos.blockState(world) - val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) - if (!isOfBlock) return + return true + } - // Check if the block above is air and other conditions for valid crystal placement - val above = pos.up() - if (!world.isAir(above)) return - if (oldPlace && !world.isAir(above.up())) return + /** + * Explodes a crystal that is on [blockPos] + * @return Whether the delay passed, null if the interaction failed or no crystal found + */ + fun explode(): Boolean? { + val timePassed = explodeTimer.timePassed(explodeDelay) + if (!timePassed) return false + + runSafe { + crystal?.let { crystal -> + explodeInternal(crystal.id) + explodeTimer.reset() + } + } ?: return null - // Exclude blocks blocked by entities - val checkBox = Box(above).withMaxY(above.y + crystalHeight) - val entitiesAbove = world.getOtherEntities(null, checkBox) - if (entitiesAbove.any { it !is EndCrystalEntity }) return + return true + } - mapDamage(pos, target) + fun buildDebug() { + withVertexTransform(buildWorldProjection(blockPos.crystalPosition, 0.4, Matrices.ProjRotationMode.TO_CAMERA)) { + val lines = arrayOf( + "Decision: ${actionType.name} ${priority.roundToStep(0.01)}", + "", + "Blocked: $blocked", + "Crystal: $hasCrystal", + "", + "Target Damage: ${target.roundToStep(0.01)}", + "Self Damage: ${self.roundToStep(0.01)}" + ) + + var height = -0.5 * lines.size * (font.getHeight() + 2) + + lines.forEach { + font.build(it, Vec2d(-font.getWidth(it) * 0.5, height)) + height += font.getHeight() + 2 + } + } + } } - private fun SafeContext.mapDamage(pos: BlockPos, target: LivingEntity) { - val crystalPos = pos.crystalPosition + private fun getBestOpportunity() = + actionMap[actionType]?.maxByOrNull { + it.priority + } - // Calculate the damage to the target from the explosion of the crystal - val targetDamage = explosionDamage(crystalPos, target, EXPLOSION_STRENGTH) - if (targetDamage < minTargetDamage) return + private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand): Boolean { + val angles = player.eyePos.rotationTo(blockPos.crystalPosition) + val cast = angles.rayCast(placeRange, mask = RayCastMask.BLOCK)?.blockResult ?: return false + if (cast.blockPos != blockPos) return false - // Calculate the self-damage for the player - val selfDamage = explosionDamage(crystalPos, player, EXPLOSION_STRENGTH) - if (selfDamage > maxSelfDamage) return + val actionResult = interaction.interactBlock(player, hand, cast) + if (!actionResult.isAccepted || !actionResult.shouldSwingHand()) return false - val immutablePos = pos.toImmutable() + player.swingHand(hand) + return true + } - // Return the calculated damage info if conditions are met - damage[immutablePos] = DamageInfo( - immutablePos, - targetDamage, - selfDamage + private fun SafeContext.explodeInternal(id: Int) { + connection.sendPacket( + PlayerInteractEntityC2SPacket( + id, player.isSneaking, PlayerInteractEntityC2SPacket.ATTACK + ) ) + + player.swingHand(Hand.MAIN_HAND) } private val EndCrystalEntity.baseBlockPos get() = @@ -278,20 +467,54 @@ object CrystalAura : Module( private val BlockPos.crystalPosition get() = this.getHitVec(Direction.UP) - private operator fun MutableMap.get(entity: EndCrystalEntity) = - get(entity.baseBlockPos) + private val BlockPos.crystalPlaceHitbox get() = + crystalPosition.let { base -> + Box( + base - Vec3d(1.0, 0.0, 1.0), + base + Vec3d(1.0, 2.0, 1.0), + ) + } - /** - * Represents the damage information resulting from placing an end crystal on a given [blockPos] - * and causing an explosion that targets current target entity. - * - * @property blockPos The position of the base block where the crystal is placed. - * @property target The amount of damage inflicted on the target. - * @property self The amount of damage inflicted on the player. - */ - private open class DamageInfo( - val blockPos: BlockPos, - val target: Double, - val self: Double, - ) + private val BlockPos.crystalBox get() = + crystalPosition.let { base -> + Box( + base - Vec3d(0.5, 0.0, 0.5), + base + Vec3d(0.5, 2.0, 0.5), + ) + } + + private enum class Page { + General, + Placement, + Prediction, + Targeting + } + + private enum class UpdateMode { + Async, + Ticked + } + + private enum class PredictionMode(val onPacket: Boolean, val onPlace: Boolean) { + None(false, false), + Packet(true, false), + Deferred(false, true); + + val isActive = onPacket || onPlace + } + + private enum class Priority(val factor: (targetDamage: Double, selfDamage: Double) -> Double) { + Damage({ target, _ -> + target + }), + Advantage({ target, self -> + target - self + }) + } + + private enum class ActionType(val priority: Int) { + Normal(0), + ForcePlace(1), + SlowBreak(2) + } } diff --git a/common/src/main/kotlin/com/lambda/util/combat/CombatUtils.kt b/common/src/main/kotlin/com/lambda/util/combat/CombatUtils.kt index 56d86a32c..e3bd840f2 100644 --- a/common/src/main/kotlin/com/lambda/util/combat/CombatUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/combat/CombatUtils.kt @@ -70,6 +70,14 @@ object CombatUtils { fastEntitySearch(12.0) .any { player.health - explosionDamage(it.pos, player, 6.0) <= minHealth} + /** + * Calculates the damage dealt by an explosion to a living entity + * @param position The position of the explosion + * @param entity The entity to calculate the damage for + */ + fun SafeContext.crystalDamage(position: Vec3d, entity: LivingEntity) = + explosionDamage(position, entity, 6.0) + /** * Calculates the damage dealt by an explosion to a living entity * @@ -113,7 +121,7 @@ object CombatUtils { * @param entity The entity to calculate the velocity for * @param explosion The explosion to calculate the velocity for */ - fun SafeContext.explosionVelocity(entity: LivingEntity, explosion: Explosion) = + fun explosionVelocity(entity: LivingEntity, explosion: Explosion) = explosionVelocity(entity, explosion.position, explosion.power.toDouble()) /** diff --git a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt index 370de99cc..8784cf45b 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt @@ -17,6 +17,7 @@ package com.lambda.util.math +import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.MathUtils.sq import net.minecraft.entity.Entity import net.minecraft.util.math.* @@ -24,8 +25,12 @@ import kotlin.math.pow import kotlin.math.roundToInt import kotlin.math.sqrt +fun BlockPos.getHitVec(side: Direction): Vec3d = + side.hitVecOffset + this + /* Direction */ -val Direction.hitVecOffset: Vec3d get() = CENTER + vector * 0.5 +val Direction.hitVecOffset: Vec3d get() = + CENTER + vector.vec3d * 0.5 fun EightWayDirection.rotateClockwise(steps: Int) = EightWayDirection.entries[(ordinal + steps) % 8] @@ -41,6 +46,12 @@ fun Vec3d.approximate(other: Vec3d, precision: Double = 2.0E-4): Boolean = val Vec3d.roundedBlockPos: BlockPos get() = BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt()) +val Vec3d.flooredBlockPos: BlockPos + get() = BlockPos(x.floorToInt(), y.floorToInt(), z.floorToInt()) + +val Entity.netherCoord: Vec3d get() = pos.multiply(0.125, 1.0, 0.125) +val Entity.overworldCoord: Vec3d get() = pos.multiply(8.0, 1.0, 8.0) + fun Vec3d.interpolate(value: Double, max: Vec3d) = lerp(value, this, max) operator fun Vec3d.component1() = x @@ -58,16 +69,19 @@ infix operator fun Vec3d.plus(other: Vec3i): Vec3d = Vec3d(x + other.x, y + othe infix operator fun Vec3d.plus(other: Double): Vec3d = add(other, other, other) infix operator fun Vec3d.plus(other: Float): Vec3d = add(other.toDouble(), other.toDouble(), other.toDouble()) infix operator fun Vec3d.plus(other: Int): Vec3d = add(other.toDouble(), other.toDouble(), other.toDouble()) + infix operator fun Vec3d.minus(other: Vec3d): Vec3d = subtract(other) infix operator fun Vec3d.minus(other: Vec3i): Vec3d = Vec3d(x - other.x, y - other.y, z - other.z) infix operator fun Vec3d.minus(other: Double): Vec3d = subtract(other, other, other) infix operator fun Vec3d.minus(other: Float): Vec3d = subtract(other.toDouble(), other.toDouble(), other.toDouble()) infix operator fun Vec3d.minus(other: Int): Vec3d = subtract(other.toDouble(), other.toDouble(), other.toDouble()) + infix operator fun Vec3d.times(other: Vec3d): Vec3d = multiply(other) infix operator fun Vec3d.times(other: Vec3i): Vec3d = Vec3d(x * other.x, y * other.y, z * other.z) infix operator fun Vec3d.times(other: Double): Vec3d = multiply(other) infix operator fun Vec3d.times(other: Float): Vec3d = multiply(other.toDouble()) infix operator fun Vec3d.times(other: Int): Vec3d = multiply(other.toDouble()) + infix operator fun Vec3d.div(other: Vec3d): Vec3d = multiply(1.0 / other.x, 1.0 / other.y, 1.0 / other.z) infix operator fun Vec3d.div(other: Vec3i): Vec3d = Vec3d(x / other.x, y / other.y, z / other.z) infix operator fun Vec3d.div(other: Double): Vec3d = times(1 / other) @@ -75,33 +89,24 @@ infix operator fun Vec3d.div(other: Float): Vec3d = times(1 / other) infix operator fun Vec3d.div(other: Int): Vec3d = times(1 / other) /* Vec3i */ -fun BlockPos.getHitVec(side: Direction): Vec3d = - side.hitVecOffset + this +val Vec3i.vec3d get() = + Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) infix fun Vec3i.dist(other: Vec3d): Double = sqrt(this distSq other) infix fun Vec3i.dist(other: Vec3i): Double = sqrt((this distSq other).toDouble()) infix fun Vec3i.distSq(other: Vec3d): Double = getSquaredDistance(other) infix fun Vec3i.distSq(other: Vec3i): Int = (x - other.x).sq + (y - other.y).sq + (z - other.z).sq -infix operator fun Vec3i.plus(other: Vec3d): Vec3i = Vec3i(x + other.x.toInt(), y + other.y.toInt(), z + other.z.toInt()) infix operator fun Vec3i.plus(other: Vec3i): Vec3i = add(other) -infix operator fun Vec3i.plus(other: Double): Vec3i = add(other.toInt(), other.toInt(), other.toInt()) -infix operator fun Vec3i.plus(other: Float): Vec3i = add(other.toInt(), other.toInt(), other.toInt()) infix operator fun Vec3i.plus(other: Int): Vec3i = add(other, other, other) -infix operator fun Vec3i.minus(other: Vec3d): Vec3i = Vec3i(x - other.x.toInt(), y - other.y.toInt(), z - other.z.toInt()) + infix operator fun Vec3i.minus(other: Vec3i): Vec3i = subtract(other) -infix operator fun Vec3i.minus(other: Double): Vec3i = add(-other.toInt(), -other.toInt(), -other.toInt()) -infix operator fun Vec3i.minus(other: Float): Vec3i = add(-other.toInt(), -other.toInt(), -other.toInt()) infix operator fun Vec3i.minus(other: Int): Vec3i = add(-other, -other, -other) -infix operator fun Vec3i.times(other: Vec3d): Vec3i = Vec3i(x * other.x.toInt(), y * other.y.toInt(), z * other.z.toInt()) + infix operator fun Vec3i.times(other: Vec3i): Vec3i = Vec3i(x * other.x, y * other.y, z * other.z) -infix operator fun Vec3i.times(other: Double): Vec3i = multiply(other.toInt()) -infix operator fun Vec3i.times(other: Float): Vec3i = multiply(other.toInt()) infix operator fun Vec3i.times(other: Int): Vec3i = multiply(other) -infix operator fun Vec3i.div(other: Vec3d): Vec3i = Vec3i((x / other.x).toInt(), (y / other.y).toInt(), (z / other.z).toInt()) + infix operator fun Vec3i.div(other: Vec3i): Vec3i = Vec3i(x / other.x, y / other.y, z / other.z) -infix operator fun Vec3i.div(other: Double): Vec3i = times(1 / other) -infix operator fun Vec3i.div(other: Float): Vec3i = times(1 / other) infix operator fun Vec3i.div(other: Int): Vec3i = times(1 / other) /* Entity */ diff --git a/gradle.properties b/gradle.properties index bcdb3613c..e6b81c37b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -51,6 +51,6 @@ kotlinForgeVersionMax=4.11.0 kotlin.code.style=official # Gradle https://gradle.org/ -org.gradle.jvmargs=-Xmx8192M \ +org.gradle.jvmargs=-Xmx4096M \ -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From fd891860731ca0e0bb0a906fc9ee633b3792abe7 Mon Sep 17 00:00:00 2001 From: blade Date: Sun, 19 Jan 2025 21:53:41 +0300 Subject: [PATCH 37/69] OP Predictions --- .../module/modules/combat/CrystalAura.kt | 140 ++++++++++-------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 29a5a5756..4689a0b78 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -30,7 +30,6 @@ import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.font.LambdaEmoji import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.interaction.RotationManager.rotate -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -43,14 +42,14 @@ import com.lambda.util.math.* import com.lambda.util.math.MathUtils.ceilToInt import com.lambda.util.math.MathUtils.roundToStep import com.lambda.util.world.fastEntitySearch -import com.lambda.util.world.raycast.RayCastMask -import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.Blocks import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.EndCrystalEntity +import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.* import kotlin.concurrent.fixedRateTimer import kotlin.math.max @@ -83,7 +82,6 @@ object CrystalAura : Module( /* Prediction */ private val prediction by setting("Prediction", PredictionMode.None) { page == Page.Prediction } private val predictionPackets by setting("Prediction Packets", 1, 1..20) { page == Page.Prediction && prediction.isActive } - private val postPlace by setting("Post Place", false) { page == Page.Prediction && prediction.isActive && predictionPackets > 1 } private val packetLifetime by setting("Packet Lifetime", 300L, 50L..1000L) { page == Page.Prediction && prediction.onPlace } /* Targeting */ @@ -105,7 +103,6 @@ object CrystalAura : Module( private val placeTimer = SimpleTimer() private val explodeTimer = SimpleTimer() - // ToDo: pending prediction decay queue (crystalId s) private val predictionTimer = SimpleTimer() private var lastEntityId = 0 @@ -171,10 +168,10 @@ object CrystalAura : Module( if (getBestOpportunity() != opportunity) return@listen repeat(predictionPackets) { - val offset = if (postPlace) 0 else it + val offset = if (prediction.postPlace) 0 else it explodeInternal(lastEntityId + offset) - if (postPlace) { + if (prediction.postPlace) { placeInternal(pos, Hand.MAIN_HAND) lastEntityId++ placeTimer.reset() @@ -187,7 +184,6 @@ object CrystalAura : Module( val pos = crystal.baseBlockPos blueprint[pos]?.crystal = null - //pendingExplosions.remove(blockPos) } onEnable { @@ -211,26 +207,7 @@ object CrystalAura : Module( // Choosing and running the best opportunity val best = getBestOpportunity() ?: return - if (!best.blocked) { - best.explode() - best.place() - return - } - - val mutableBlockPos = BlockPos.Mutable() - - // Break crystals nearby if the best crystal placement is blocked by other crystals - collidingOffsets.mapNotNull { - mutableBlockPos.set( - best.blockPos.x + it.x, - best.blockPos.y + it.y, - best.blockPos.z + it.z - ) - - blueprint[mutableBlockPos] - }.filter { it.hasCrystal }.maxByOrNull { it.priority }?.explode() - - best.place() + tickInteraction(best) } private fun SafeContext.buildBlueprint(target: LivingEntity) = updateTimer.runIfPassed(updateDelay) { @@ -335,8 +312,6 @@ object CrystalAura : Module( blueprint[it.blockPos] = it } - // ToDo: force place - // Associate by actions blueprint.values.forEach { opportunity -> actionMap.getOrPut(opportunity.actionType, ::mutableListOf) += opportunity @@ -347,6 +322,54 @@ object CrystalAura : Module( } } + private fun tickInteraction(best: Opportunity) { + if (!best.blocked) { + best.explode() + best.place() + return + } + + val mutableBlockPos = BlockPos.Mutable() + + // Break crystals nearby if the best crystal placement is blocked by other crystals + collidingOffsets.mapNotNull { + mutableBlockPos.set( + best.blockPos.x + it.x, + best.blockPos.y + it.y, + best.blockPos.z + it.z + ) + + blueprint[mutableBlockPos] + }.filter { it.hasCrystal }.maxByOrNull { it.priority }?.explode() + + best.place() + } + + private fun getBestOpportunity() = + actionMap[actionType]?.maxByOrNull { + it.priority + } + + private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand) { + connection.sendPacket( + PlayerInteractBlockC2SPacket( + hand, BlockHitResult(blockPos.crystalPosition, Direction.UP, blockPos, false), 0 + ) + ) + + player.swingHand(hand) + } + + private fun SafeContext.explodeInternal(id: Int) { + connection.sendPacket( + PlayerInteractEntityC2SPacket( + id, player.isSneaking, PlayerInteractEntityC2SPacket.ATTACK + ) + ) + + player.swingHand(Hand.MAIN_HAND) + } + /** * Represents the damage information resulting from placing an end crystal on a given [blockPos] * and causing an explosion that targets current target entity. @@ -366,7 +389,7 @@ object CrystalAura : Module( ) { var actionType = ActionType.Normal val priority = priorityMode.factor(target, self) - val hasCrystal = crystal != null + val hasCrystal get() = crystal != null /** * Places the crystal on [blockPos] @@ -382,10 +405,19 @@ object CrystalAura : Module( run { if (!prediction.onPlace || predictionTimer.timePassed(packetLifetime)) return@run + val last = lastEntityId + repeat(predictionPackets) { - if (it != 0) placeInternal(blockPos, Hand.MAIN_HAND) + if (it != 0 && prediction.postPlace) { + placeInternal(blockPos, Hand.MAIN_HAND) + } explodeInternal(++lastEntityId) } + + if (prediction == PredictionMode.StepDeferred) { + lastEntityId = last + 1 + crystal = null + } } placeTimer.reset() @@ -434,33 +466,6 @@ object CrystalAura : Module( } } - private fun getBestOpportunity() = - actionMap[actionType]?.maxByOrNull { - it.priority - } - - private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand): Boolean { - val angles = player.eyePos.rotationTo(blockPos.crystalPosition) - val cast = angles.rayCast(placeRange, mask = RayCastMask.BLOCK)?.blockResult ?: return false - if (cast.blockPos != blockPos) return false - - val actionResult = interaction.interactBlock(player, hand, cast) - if (!actionResult.isAccepted || !actionResult.shouldSwingHand()) return false - - player.swingHand(hand) - return true - } - - private fun SafeContext.explodeInternal(id: Int) { - connection.sendPacket( - PlayerInteractEntityC2SPacket( - id, player.isSneaking, PlayerInteractEntityC2SPacket.ATTACK - ) - ) - - player.swingHand(Hand.MAIN_HAND) - } - private val EndCrystalEntity.baseBlockPos get() = (pos - Vec3d(0.0, 0.5, 0.0)).flooredBlockPos @@ -495,10 +500,18 @@ object CrystalAura : Module( Ticked } - private enum class PredictionMode(val onPacket: Boolean, val onPlace: Boolean) { - None(false, false), - Packet(true, false), - Deferred(false, true); + private enum class PredictionMode(val onPacket: Boolean, val onPlace: Boolean, val postPlace: Boolean) { + // Prediction disable + None(false, false, false), + + // Predict on packet receive + SemiPacket(true, false, false), + Packet(true, false, true), + + // Predict on place + SemiDeferred(false, true, false), + Deferred(false, true, true), + StepDeferred(false, true, false); // the best method val isActive = onPacket || onPlace } @@ -512,6 +525,7 @@ object CrystalAura : Module( }) } + // ToDo: implement actions private enum class ActionType(val priority: Int) { Normal(0), ForcePlace(1), From d72809ccfa41a594c5af85ae4aaabfbcaa3fbe38 Mon Sep 17 00:00:00 2001 From: blade Date: Mon, 20 Jan 2025 02:36:57 +0300 Subject: [PATCH 38/69] Rotation & refactor --- .../module/modules/combat/CrystalAura.kt | 232 ++++++++++-------- .../kotlin/com/lambda/util/SimpleTimer.kt | 55 +++++ 2 files changed, 181 insertions(+), 106 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 4689a0b78..30cb3150b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -17,6 +17,7 @@ package com.lambda.module.modules.combat +import com.lambda.config.groups.RotationSettings import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext import com.lambda.event.events.EntityEvent @@ -30,6 +31,7 @@ import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.font.LambdaEmoji import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.interaction.RotationManager.rotate +import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationRequest import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -68,6 +70,7 @@ object CrystalAura : Module( private val explodeDelay by setting("Explode Delay", 10L, 0L..1000L, 5L, "Delay between explosion attempts", " ms") { page == Page.General } private val updateMode by setting("Update Mode", UpdateMode.Async) { page == Page.General } private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L) { page == Page.General && updateMode == UpdateMode.Async } + private val maxUpdatesPerFrame by setting("Max Updates Per Frame", 5, 1..20, 1) { page == Page.General && updateMode == UpdateMode.Async } private val updateDelay get() = if (updateMode == UpdateMode.Async) updateDelaySetting else 0L private val debug by setting("Debug", false) { page == Page.General } @@ -88,18 +91,19 @@ object CrystalAura : Module( private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting } /* Rotation */ - //private val rotateToPlace by setting("Rotate To Place", true) { page == Page.Rotation } - //private val rotateToExplode by setting("Rotate To Explode", true) { page == Page.Rotation } - //private val rotation = RotationSettings(this) { page == Page.Rotation } - private var rotationTarget: RotationRequest? = null + private val rotation = RotationSettings(this) { page == Page.Rotation } private val blueprint = mutableMapOf() + private var activeOpportunity: Opportunity? = null + private var currentTarget: LivingEntity? = null private val damage = mutableListOf() private val actionMap = mutableMapOf>() private var actionType = ActionType.Normal private val updateTimer = SimpleTimer() + private var updatesThisFrame = 0 + private val placeTimer = SimpleTimer() private val explodeTimer = SimpleTimer() @@ -124,12 +128,19 @@ object CrystalAura : Module( name = "Crystal Aura Thread", daemon = true, initialDelay = 0L, - period = 3L + period = 1L ) { if (CrystalAura.isDisabled || updateMode != UpdateMode.Async) return@fixedRateTimer - runSafeGameScheduled { - tick() + runSafe { + // timer may spam faster than main thread computes(game freezes completely at the beginning of the frame) + if (updatesThisFrame > maxUpdatesPerFrame) return@runSafe + updatesThisFrame++ + + // run this safely again to ensure that the context will stay safe at the next frame + runSafeGameScheduled { + tick() + } } } @@ -138,6 +149,10 @@ object CrystalAura : Module( if (updateMode == UpdateMode.Ticked) tick() } + listen { + updatesThisFrame = 0 + } + listen { if (!debug) return@listen @@ -154,19 +169,21 @@ object CrystalAura : Module( } } + // Prediction listen { event -> + // Update last received entity spawn lastEntityId = event.entity.id predictionTimer.reset() val crystal = event.entity as? EndCrystalEntity ?: return@listen val pos = crystal.baseBlockPos + // Update crystal val opportunity = blueprint[pos] ?: return@listen opportunity.crystal = crystal - if (!prediction.onPacket) return@listen - if (getBestOpportunity() != opportunity) return@listen - + // Run packet prediction + if (!prediction.onPacket || activeOpportunity != opportunity) return@listen repeat(predictionPackets) { val offset = if (prediction.postPlace) 0 else it explodeInternal(lastEntityId + offset) @@ -183,38 +200,88 @@ object CrystalAura : Module( val crystal = event.entity as? EndCrystalEntity ?: return@listen val pos = crystal.baseBlockPos + // Invalidate crystal entity blueprint[pos]?.crystal = null } onEnable { - lastEntityId = -1 + currentTarget = null + resetBlueprint() } rotate { - request { rotationTarget } + request { + activeOpportunity?.let { + RotationRequest( + player.eyePos.rotationTo( + it.blockPos.crystalPosition + ), rotation + ) + } + } } } private fun SafeContext.tick() { - val target = targeting.target() ?: run { - blueprint.clear() - return - } + // Update the target + currentTarget = targeting.target() // Update the blueprint - buildBlueprint(target) + currentTarget?.let { + updateBlueprint(it) + } ?: resetBlueprint() // Choosing and running the best opportunity - val best = getBestOpportunity() ?: return + activeOpportunity?.let { + tickInteraction(it) + } + } - tickInteraction(best) + private fun tickInteraction(best: Opportunity) { + if (!best.blocked) { + best.explode() + best.place() + return + } + + val mutableBlockPos = BlockPos.Mutable() + + // Break crystals nearby if the best crystal placement is blocked by other crystals + collidingOffsets.mapNotNull { + mutableBlockPos.set( + best.blockPos.x + it.x, + best.blockPos.y + it.y, + best.blockPos.z + it.z + ) + + blueprint[mutableBlockPos] + }.filter { it.hasCrystal }.maxByOrNull { it.priority }?.explode() + + best.place() } - private fun SafeContext.buildBlueprint(target: LivingEntity) = updateTimer.runIfPassed(updateDelay) { - blueprint.clear() - damage.clear() - actionMap.clear() - actionType = ActionType.Normal + private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand) { + connection.sendPacket( + PlayerInteractBlockC2SPacket( + hand, BlockHitResult(blockPos.crystalPosition, Direction.UP, blockPos, false), 0 + ) + ) + + player.swingHand(hand) + } + + private fun SafeContext.explodeInternal(id: Int) { + connection.sendPacket( + PlayerInteractEntityC2SPacket( + id, player.isSneaking, PlayerInteractEntityC2SPacket.ATTACK + ) + ) + + player.swingHand(Hand.MAIN_HAND) + } + + private fun SafeContext.updateBlueprint(target: LivingEntity) = updateTimer.runIfPassed(updateDelay) { + resetBlueprint() // Build damage info fun info( @@ -244,7 +311,7 @@ object CrystalAura : Module( ) } - // Extra checks for placement, because you may explode in special cases(crystal in the air) but not place + // Extra checks for placement, because you may explode but not place in special cases(crystal in the air) @Suppress("ConvertArgumentToSet") fun placeInfo( pos: BlockPos, @@ -276,7 +343,7 @@ object CrystalAura : Module( it.baseBlockPos == pos } - val crystalPlaceBox = pos.crystalPlaceHitbox + val crystalPlaceBox = pos.crystalPlaceHitBox val blocked = baseCrystal == null && crystals.any { it.boundingBox.intersects(crystalPlaceBox) } @@ -320,54 +387,18 @@ object CrystalAura : Module( actionType = opportunity.actionType } } - } - - private fun tickInteraction(best: Opportunity) { - if (!best.blocked) { - best.explode() - best.place() - return - } - - val mutableBlockPos = BlockPos.Mutable() - - // Break crystals nearby if the best crystal placement is blocked by other crystals - collidingOffsets.mapNotNull { - mutableBlockPos.set( - best.blockPos.x + it.x, - best.blockPos.y + it.y, - best.blockPos.z + it.z - ) - - blueprint[mutableBlockPos] - }.filter { it.hasCrystal }.maxByOrNull { it.priority }?.explode() - best.place() - } - - private fun getBestOpportunity() = - actionMap[actionType]?.maxByOrNull { + // Select best action + activeOpportunity = actionMap[actionType]?.maxByOrNull { it.priority } - - private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand) { - connection.sendPacket( - PlayerInteractBlockC2SPacket( - hand, BlockHitResult(blockPos.crystalPosition, Direction.UP, blockPos, false), 0 - ) - ) - - player.swingHand(hand) } - private fun SafeContext.explodeInternal(id: Int) { - connection.sendPacket( - PlayerInteractEntityC2SPacket( - id, player.isSneaking, PlayerInteractEntityC2SPacket.ATTACK - ) - ) - - player.swingHand(Hand.MAIN_HAND) + private fun resetBlueprint() { + blueprint.clear() + damage.clear() + actionMap.clear() + activeOpportunity = null } /** @@ -395,53 +426,39 @@ object CrystalAura : Module( * Places the crystal on [blockPos] * @return Whether the delay passed, null if the interaction failed */ - fun place(): Boolean? { - val timePassed = placeTimer.timePassed(placeDelay) - if (!timePassed) return false - - runSafe { - placeInternal(blockPos, Hand.MAIN_HAND) + fun place() = placeTimer.runSafeIfPassed(placeDelay) { + placeInternal(blockPos, Hand.MAIN_HAND) + if (prediction.onPlace) predictionPlace() - run { - if (!prediction.onPlace || predictionTimer.timePassed(packetLifetime)) return@run - - val last = lastEntityId + placeTimer.reset() + } - repeat(predictionPackets) { - if (it != 0 && prediction.postPlace) { - placeInternal(blockPos, Hand.MAIN_HAND) - } - explodeInternal(++lastEntityId) - } + private fun SafeContext.predictionPlace() = predictionTimer.runIfNotPassed(packetLifetime) { + val last = lastEntityId - if (prediction == PredictionMode.StepDeferred) { - lastEntityId = last + 1 - crystal = null - } + repeat(predictionPackets) { + if (it != 0 && prediction.postPlace) { + placeInternal(blockPos, Hand.MAIN_HAND) } - placeTimer.reset() - } ?: return null + explodeInternal(++lastEntityId) + } - return true + if (prediction == PredictionMode.StepDeferred) { + lastEntityId = last + 1 + crystal = null + } } /** * Explodes a crystal that is on [blockPos] * @return Whether the delay passed, null if the interaction failed or no crystal found */ - fun explode(): Boolean? { - val timePassed = explodeTimer.timePassed(explodeDelay) - if (!timePassed) return false - - runSafe { - crystal?.let { crystal -> - explodeInternal(crystal.id) - explodeTimer.reset() - } - } ?: return null - - return true + fun explode() = explodeTimer.runSafeIfPassed(explodeDelay) { + crystal?.let { crystal -> + explodeInternal(crystal.id) + explodeTimer.reset() + } } fun buildDebug() { @@ -472,7 +489,7 @@ object CrystalAura : Module( private val BlockPos.crystalPosition get() = this.getHitVec(Direction.UP) - private val BlockPos.crystalPlaceHitbox get() = + private val BlockPos.crystalPlaceHitBox get() = crystalPosition.let { base -> Box( base - Vec3d(1.0, 0.0, 1.0), @@ -492,7 +509,8 @@ object CrystalAura : Module( General, Placement, Prediction, - Targeting + Targeting, + Rotation } private enum class UpdateMode { @@ -500,6 +518,7 @@ object CrystalAura : Module( Ticked } + @Suppress("Unused") private enum class PredictionMode(val onPacket: Boolean, val onPlace: Boolean, val postPlace: Boolean) { // Prediction disable None(false, false, false), @@ -526,6 +545,7 @@ object CrystalAura : Module( } // ToDo: implement actions + @Suppress("Unused") private enum class ActionType(val priority: Int) { Normal(0), ForcePlace(1), diff --git a/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt b/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt index 6a4fa6dd5..19b6c6b1f 100644 --- a/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt +++ b/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt @@ -17,6 +17,9 @@ package com.lambda.util +import com.lambda.context.SafeContext +import com.lambda.threading.runSafe + /** * A utility class to manage time-based operations, such as delays and periodic tasks. */ @@ -62,6 +65,58 @@ class SimpleTimer { block() } + /** + * Executes a given block of code if the specified time has not passed yet since the last timing event. + * Optionally resets the timer after execution. + * + * @param time the time interval in milliseconds to check. + * @param reset whether to reset the timer after running the block. Defaults to `true`. + * @param block the code block to execute if the time has passed. + */ + fun runIfNotPassed(time: Long, reset: Boolean = true, block: () -> Unit) = + timePassed(time).apply { + if (this) return@apply + if (reset) reset() + + block() + } + + /** + * Executes a given block of code in safe context if the specified time has passed since the last timing event. + * Optionally resets the timer after execution. + * + * @param time the time interval in milliseconds to check. + * @param reset whether to reset the timer after running the block. Defaults to `true`. + * @param block the code block to execute if the time has passed. + */ + fun runSafeIfPassed(time: Long, reset: Boolean = true, block: SafeContext.() -> Unit) = + timePassed(time).also { passed -> + if (!passed) return@also + + runSafe { + if (reset) reset() + block() + } + } + + /** + * Executes a given block of code in safe context if the specified time has not passed yet since the last timing event. + * Optionally resets the timer after execution. + * + * @param time the time interval in milliseconds to check. + * @param reset whether to reset the timer after running the block. Defaults to `true`. + * @param block the code block to execute if the time has passed. + */ + fun runSafeIfNotPassed(time: Long, reset: Boolean = true, block: SafeContext.() -> Unit) = + timePassed(time).also { passed -> + if (passed) return@also + + runSafe { + if (reset) reset() + block() + } + } + /** * Resets the timer by updating the last timing event to the current time. * From cb855482210f36c6dfcf12646624678523f6fff9 Mon Sep 17 00:00:00 2001 From: blade Date: Mon, 20 Jan 2025 04:08:59 +0300 Subject: [PATCH 39/69] Timer refactor --- common/src/main/kotlin/com/lambda/util/SimpleTimer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt b/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt index 19b6c6b1f..f9825503b 100644 --- a/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt +++ b/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt @@ -74,8 +74,8 @@ class SimpleTimer { * @param block the code block to execute if the time has passed. */ fun runIfNotPassed(time: Long, reset: Boolean = true, block: () -> Unit) = - timePassed(time).apply { - if (this) return@apply + timePassed(time).also { passed -> + if (passed) return@also if (reset) reset() block() From 1c710ae85a75e9867ed7176c9e050c392876af95 Mon Sep 17 00:00:00 2001 From: blade Date: Mon, 20 Jan 2025 04:48:43 +0300 Subject: [PATCH 40/69] Fixed & improved predictions --- .../module/modules/combat/CrystalAura.kt | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 30cb3150b..b5ed01e18 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -183,15 +183,24 @@ object CrystalAura : Module( opportunity.crystal = crystal // Run packet prediction - if (!prediction.onPacket || activeOpportunity != opportunity) return@listen - repeat(predictionPackets) { - val offset = if (prediction.postPlace) 0 else it - explodeInternal(lastEntityId + offset) - - if (prediction.postPlace) { - placeInternal(pos, Hand.MAIN_HAND) - lastEntityId++ - placeTimer.reset() + if (activeOpportunity != opportunity) return@listen + + when { + prediction.onPlace -> { + explodeInternal(lastEntityId) + } + + prediction.onPacket -> { + repeat(predictionPackets) { + val offset = if (prediction.postPlace) 0 else it + explodeInternal(lastEntityId + offset) + + if (prediction.postPlace) { + placeInternal(pos, Hand.MAIN_HAND) + lastEntityId++ + placeTimer.reset() + } + } } } } @@ -428,26 +437,25 @@ object CrystalAura : Module( */ fun place() = placeTimer.runSafeIfPassed(placeDelay) { placeInternal(blockPos, Hand.MAIN_HAND) - if (prediction.onPlace) predictionPlace() - placeTimer.reset() - } + if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime, false) { + val last = lastEntityId - private fun SafeContext.predictionPlace() = predictionTimer.runIfNotPassed(packetLifetime) { - val last = lastEntityId + repeat(predictionPackets) { + if (it != 0 && prediction.postPlace) { + placeInternal(blockPos, Hand.MAIN_HAND) + } - repeat(predictionPackets) { - if (it != 0 && prediction.postPlace) { - placeInternal(blockPos, Hand.MAIN_HAND) + explodeInternal(++lastEntityId) } - explodeInternal(++lastEntityId) + if (prediction == PredictionMode.StepDeferred) { + lastEntityId = last + 1 + crystal = null + } } - if (prediction == PredictionMode.StepDeferred) { - lastEntityId = last + 1 - crystal = null - } + placeTimer.reset() } /** From bc19659dc408f9b9180296668bc56612de4999dc Mon Sep 17 00:00:00 2001 From: blade Date: Mon, 20 Jan 2025 05:42:53 +0300 Subject: [PATCH 41/69] Cleaned up & improved predictions --- .../module/modules/combat/CrystalAura.kt | 65 ++++++++----------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index b5ed01e18..a82f83553 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -84,8 +84,9 @@ object CrystalAura : Module( /* Prediction */ private val prediction by setting("Prediction", PredictionMode.None) { page == Page.Prediction } - private val predictionPackets by setting("Prediction Packets", 1, 1..20) { page == Page.Prediction && prediction.isActive } - private val packetLifetime by setting("Packet Lifetime", 300L, 50L..1000L) { page == Page.Prediction && prediction.onPlace } + private val packetPredictions by setting("Packet Predictions", 1, 0..20, 1) { page == Page.Prediction && prediction.onPacket } + private val placePredictions by setting("Place Predictions", 4, 1..20, 1) { page == Page.Prediction && prediction.onPlace } + private val packetLifetime by setting("Packet Lifetime", 500L, 50L..1000L) { page == Page.Prediction && prediction.onPlace } /* Targeting */ private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting } @@ -169,12 +170,14 @@ object CrystalAura : Module( } } - // Prediction - listen { event -> - // Update last received entity spawn + // Update last received entity spawn + listen(alwaysListen = true) { event -> lastEntityId = event.entity.id predictionTimer.reset() + } + // Prediction + listen { event -> val crystal = event.entity as? EndCrystalEntity ?: return@listen val pos = crystal.baseBlockPos @@ -183,26 +186,18 @@ object CrystalAura : Module( opportunity.crystal = crystal // Run packet prediction - if (activeOpportunity != opportunity) return@listen + if (!prediction.isActive || activeOpportunity != opportunity) return@listen - when { - prediction.onPlace -> { - explodeInternal(lastEntityId) - } + explodeInternal(lastEntityId) - prediction.onPacket -> { - repeat(predictionPackets) { - val offset = if (prediction.postPlace) 0 else it - explodeInternal(lastEntityId + offset) - - if (prediction.postPlace) { - placeInternal(pos, Hand.MAIN_HAND) - lastEntityId++ - placeTimer.reset() - } - } - } + if (!prediction.onPacket) return@listen + + repeat(packetPredictions) { + placeInternal(pos, Hand.MAIN_HAND) + explodeInternal(++lastEntityId) } + + placeTimer.reset() } listen { event -> @@ -441,18 +436,12 @@ object CrystalAura : Module( if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime, false) { val last = lastEntityId - repeat(predictionPackets) { - if (it != 0 && prediction.postPlace) { - placeInternal(blockPos, Hand.MAIN_HAND) - } - + repeat(placePredictions) { explodeInternal(++lastEntityId) } - if (prediction == PredictionMode.StepDeferred) { - lastEntityId = last + 1 - crystal = null - } + lastEntityId = last + 1 + crystal = null } placeTimer.reset() @@ -527,18 +516,18 @@ object CrystalAura : Module( } @Suppress("Unused") - private enum class PredictionMode(val onPacket: Boolean, val onPlace: Boolean, val postPlace: Boolean) { + private enum class PredictionMode(val onPacket: Boolean, val onPlace: Boolean) { // Prediction disable - None(false, false, false), + None(false, false), // Predict on packet receive - SemiPacket(true, false, false), - Packet(true, false, true), + Packet(true, false), // Predict on place - SemiDeferred(false, true, false), - Deferred(false, true, true), - StepDeferred(false, true, false); // the best method + Deferred(false, true), + + // Predict on both timings + Mixed(true, true); val isActive = onPacket || onPlace } From 0fbd682426cb687a0ce3289a7ede8a8fd64cd5c6 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 21 Jan 2025 03:16:28 +0100 Subject: [PATCH 42/69] Accurate Timer with duration type --- .../module/modules/combat/CrystalAura.kt | 228 +++++++++--------- .../lambda/util/{SimpleTimer.kt => Timer.kt} | 74 +++--- 2 files changed, 152 insertions(+), 150 deletions(-) rename common/src/main/kotlin/com/lambda/util/{SimpleTimer.kt => Timer.kt} (53%) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index a82f83553..902e5f848 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -38,7 +38,7 @@ import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.threading.runSafeGameScheduled import com.lambda.util.BlockUtils.blockState -import com.lambda.util.SimpleTimer +import com.lambda.util.Timer import com.lambda.util.combat.CombatUtils.crystalDamage import com.lambda.util.math.* import com.lambda.util.math.MathUtils.ceilToInt @@ -55,6 +55,8 @@ import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.* import kotlin.concurrent.fixedRateTimer import kotlin.math.max +import kotlin.time.DurationUnit +import kotlin.time.toDuration object CrystalAura : Module( name = "CrystalAura", @@ -69,7 +71,7 @@ object CrystalAura : Module( private val placeDelay by setting("Place Delay", 50L, 0L..1000L, 5L, "Delay between placement attempts", " ms") { page == Page.General } private val explodeDelay by setting("Explode Delay", 10L, 0L..1000L, 5L, "Delay between explosion attempts", " ms") { page == Page.General } private val updateMode by setting("Update Mode", UpdateMode.Async) { page == Page.General } - private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L) { page == Page.General && updateMode == UpdateMode.Async } + private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L, unit = " ms") { page == Page.General && updateMode == UpdateMode.Async } private val maxUpdatesPerFrame by setting("Max Updates Per Frame", 5, 1..20, 1) { page == Page.General && updateMode == UpdateMode.Async } private val updateDelay get() = if (updateMode == UpdateMode.Async) updateDelaySetting else 0L private val debug by setting("Debug", false) { page == Page.General } @@ -102,13 +104,13 @@ object CrystalAura : Module( private val actionMap = mutableMapOf>() private var actionType = ActionType.Normal - private val updateTimer = SimpleTimer() + private val updateTimer = Timer() private var updatesThisFrame = 0 - private val placeTimer = SimpleTimer() - private val explodeTimer = SimpleTimer() + private val placeTimer = Timer() + private val explodeTimer = Timer() - private val predictionTimer = SimpleTimer() + private val predictionTimer = Timer() private var lastEntityId = 0 private val collidingOffsets = mutableListOf().apply { @@ -284,119 +286,120 @@ object CrystalAura : Module( player.swingHand(Hand.MAIN_HAND) } - private fun SafeContext.updateBlueprint(target: LivingEntity) = updateTimer.runIfPassed(updateDelay) { - resetBlueprint() - - // Build damage info - fun info( - pos: BlockPos, target: LivingEntity, - blocked: Boolean, - crystal: EndCrystalEntity? = null - ): Opportunity? { - val crystalPos = pos.crystalPosition - - // Calculate the damage to the target from the explosion of the crystal - val targetDamage = crystalDamage(crystalPos, target) - if (targetDamage < minTargetDamage) return null - - // Calculate the self-damage for the player - val selfDamage = crystalDamage(crystalPos, player) - if (selfDamage > maxSelfDamage) return null - - if (priorityMode == Priority.Advantage && priorityMode.factor(targetDamage, selfDamage) < minDamageAdvantage) return null - - // Return the calculated damage info if conditions are met - return Opportunity( - pos.toImmutable(), - targetDamage, - selfDamage, - blocked, - crystal - ) - } + private fun SafeContext.updateBlueprint(target: LivingEntity) = + updateTimer.runIfPassed(updateDelay.toDuration(DurationUnit.MILLISECONDS)) { + resetBlueprint() - // Extra checks for placement, because you may explode but not place in special cases(crystal in the air) - @Suppress("ConvertArgumentToSet") - fun placeInfo( - pos: BlockPos, - target: LivingEntity - ): Opportunity? { - // Check if crystals could be placed on the base block - val state = pos.blockState(world) - val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) - if (!isOfBlock) return null - - // Check if the block above is air and other conditions for valid crystal placement - val above = pos.up() - if (!world.isAir(above)) return null - if (oldPlace && !world.isAir(above.up())) return null - - // Exclude blocks blocked by entities - val crystalBox = pos.crystalBox - - val entitiesNearby = fastEntitySearch(3.5, pos) - val crystals = entitiesNearby.filterIsInstance() as MutableList - val otherEntities = entitiesNearby - crystals + player - - if (otherEntities.any { - it.boundingBox.intersects(crystalBox) - }) return null - - // Placement collision checks - val baseCrystal = crystals.firstOrNull { - it.baseBlockPos == pos + // Build damage info + fun info( + pos: BlockPos, target: LivingEntity, + blocked: Boolean, + crystal: EndCrystalEntity? = null + ): Opportunity? { + val crystalPos = pos.crystalPosition + + // Calculate the damage to the target from the explosion of the crystal + val targetDamage = crystalDamage(crystalPos, target) + if (targetDamage < minTargetDamage) return null + + // Calculate the self-damage for the player + val selfDamage = crystalDamage(crystalPos, player) + if (selfDamage > maxSelfDamage) return null + + if (priorityMode == Priority.Advantage && priorityMode.factor(targetDamage, selfDamage) < minDamageAdvantage) return null + + // Return the calculated damage info if conditions are met + return Opportunity( + pos.toImmutable(), + targetDamage, + selfDamage, + blocked, + crystal + ) } - val crystalPlaceBox = pos.crystalPlaceHitBox - val blocked = baseCrystal == null && crystals.any { - it.boundingBox.intersects(crystalPlaceBox) - } + // Extra checks for placement, because you may explode but not place in special cases(crystal in the air) + @Suppress("ConvertArgumentToSet") + fun placeInfo( + pos: BlockPos, + target: LivingEntity + ): Opportunity? { + // Check if crystals could be placed on the base block + val state = pos.blockState(world) + val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) + if (!isOfBlock) return null + + // Check if the block above is air and other conditions for valid crystal placement + val above = pos.up() + if (!world.isAir(above)) return null + if (oldPlace && !world.isAir(above.up())) return null + + // Exclude blocks blocked by entities + val crystalBox = pos.crystalBox + + val entitiesNearby = fastEntitySearch(3.5, pos) + val crystals = entitiesNearby.filterIsInstance() as MutableList + val otherEntities = entitiesNearby - crystals + player + + if (otherEntities.any { + it.boundingBox.intersects(crystalBox) + }) return null + + // Placement collision checks + val baseCrystal = crystals.firstOrNull { + it.baseBlockPos == pos + } - return info( - pos, - target, - blocked, - baseCrystal - ) - } + val crystalPlaceBox = pos.crystalPlaceHitBox + val blocked = baseCrystal == null && crystals.any { + it.boundingBox.intersects(crystalPlaceBox) + } - val range = max(placeRange, explodeRange) + 1 - val rangeInt = range.ceilToInt() + return info( + pos, + target, + blocked, + baseCrystal + ) + } - // Iterate through existing crystals - val crystalBase = BlockPos.Mutable() - fastEntitySearch(range).forEach { crystal -> - crystalBase.set(crystal.x, crystal.y - 0.5, crystal.z) - damage += info(crystalBase, target, false, crystal) ?: return@forEach - } + val range = max(placeRange, explodeRange) + 1 + val rangeInt = range.ceilToInt() - // Iterate through possible place positions and calculate damage information for each - BlockPos.iterateOutwards(player.blockPos.up(), rangeInt, rangeInt, rangeInt).forEach { pos -> - if (pos distSq player.pos > range * range) return@forEach - if (damage.any { info -> info.blockPos == pos }) return@forEach + // Iterate through existing crystals + val crystalBase = BlockPos.Mutable() + fastEntitySearch(range).forEach { crystal -> + crystalBase.set(crystal.x, crystal.y - 0.5, crystal.z) + damage += info(crystalBase, target, false, crystal) ?: return@forEach + } - damage += placeInfo(pos, target) ?: return@forEach - } + // Iterate through possible place positions and calculate damage information for each + BlockPos.iterateOutwards(player.blockPos.up(), rangeInt, rangeInt, rangeInt).forEach { pos -> + if (pos distSq player.pos > range * range) return@forEach + if (damage.any { info -> info.blockPos == pos }) return@forEach - // Map opportunities - damage.forEach { - blueprint[it.blockPos] = it - } + damage += placeInfo(pos, target) ?: return@forEach + } + + // Map opportunities + damage.forEach { + blueprint[it.blockPos] = it + } - // Associate by actions - blueprint.values.forEach { opportunity -> - actionMap.getOrPut(opportunity.actionType, ::mutableListOf) += opportunity + // Associate by actions + blueprint.values.forEach { opportunity -> + actionMap.getOrPut(opportunity.actionType, ::mutableListOf) += opportunity - if (opportunity.actionType.priority > actionType.priority) { - actionType = opportunity.actionType + if (opportunity.actionType.priority > actionType.priority) { + actionType = opportunity.actionType + } } - } - // Select best action - activeOpportunity = actionMap[actionType]?.maxByOrNull { - it.priority + // Select best action + activeOpportunity = actionMap[actionType]?.maxByOrNull { + it.priority + } } - } private fun resetBlueprint() { blueprint.clear() @@ -430,10 +433,10 @@ object CrystalAura : Module( * Places the crystal on [blockPos] * @return Whether the delay passed, null if the interaction failed */ - fun place() = placeTimer.runSafeIfPassed(placeDelay) { + fun place() = placeTimer.runSafeIfPassed(placeDelay.toDuration(DurationUnit.MILLISECONDS)) { placeInternal(blockPos, Hand.MAIN_HAND) - if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime, false) { + if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime.toDuration(DurationUnit.MILLISECONDS), false) { val last = lastEntityId repeat(placePredictions) { @@ -451,12 +454,13 @@ object CrystalAura : Module( * Explodes a crystal that is on [blockPos] * @return Whether the delay passed, null if the interaction failed or no crystal found */ - fun explode() = explodeTimer.runSafeIfPassed(explodeDelay) { - crystal?.let { crystal -> - explodeInternal(crystal.id) - explodeTimer.reset() + fun explode() = + explodeTimer.runSafeIfPassed(explodeDelay.toDuration(DurationUnit.MILLISECONDS)) { + crystal?.let { crystal -> + explodeInternal(crystal.id) + explodeTimer.reset() + } } - } fun buildDebug() { withVertexTransform(buildWorldProjection(blockPos.crystalPosition, 0.4, Matrices.ProjRotationMode.TO_CAMERA)) { diff --git a/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt b/common/src/main/kotlin/com/lambda/util/Timer.kt similarity index 53% rename from common/src/main/kotlin/com/lambda/util/SimpleTimer.kt rename to common/src/main/kotlin/com/lambda/util/Timer.kt index f9825503b..e92f3c054 100644 --- a/common/src/main/kotlin/com/lambda/util/SimpleTimer.kt +++ b/common/src/main/kotlin/com/lambda/util/Timer.kt @@ -19,46 +19,48 @@ package com.lambda.util import com.lambda.context.SafeContext import com.lambda.threading.runSafe +import kotlin.time.Duration +import kotlin.time.TimeSource /** * A utility class to manage time-based operations, such as delays and periodic tasks. */ -class SimpleTimer { +class Timer { /** - * Stores the timestamp of the last timing event. + * Records the timestamp of the last timing event using a monotonic clock. */ - private var lastTiming = 0L + private var lastTiming = TimeSource.Monotonic.markNow() /** * Checks if the specified amount of time has passed since the last timing event. * - * @param time the time interval in milliseconds to check. - * @return `true` if the current time exceeds `lastTiming + time`, `false` otherwise. + * @param duration the time interval to check. + * @return `true` if the current time exceeds `lastTiming + duration`, `false` otherwise. */ - fun timePassed(time: Long): Boolean = - currentTime - lastTiming > time + fun timePassed(duration: Duration): Boolean = + lastTiming.elapsedNow() > duration /** - * Checks if the specified time has passed and resets the timer if true. + * Checks if the specified duration has passed and resets the timer if true. * - * @param time the time interval in milliseconds to check. - * @return `true` if the time has passed and the timer was reset, `false` otherwise. + * @param duration the time interval to check. + * @return `true` if the duration has passed and the timer was reset, `false` otherwise. */ - fun delayIfPassed(time: Long): Boolean = - timePassed(time).apply { + fun delayIfPassed(duration: Duration): Boolean = + timePassed(duration).apply { if (this) reset() } /** - * Executes a given block of code if the specified time has passed since the last timing event. + * Executes a given block of code if the specified duration has passed since the last timing event. * Optionally resets the timer after execution. * - * @param time the time interval in milliseconds to check. + * @param duration the time interval to check. * @param reset whether to reset the timer after running the block. Defaults to `true`. * @param block the code block to execute if the time has passed. */ - fun runIfPassed(time: Long, reset: Boolean = true, block: () -> Unit) = - timePassed(time).apply { + fun runIfPassed(duration: Duration, reset: Boolean = true, block: () -> Unit) = + timePassed(duration).apply { if (!this) return@apply if (reset) reset() @@ -66,15 +68,15 @@ class SimpleTimer { } /** - * Executes a given block of code if the specified time has not passed yet since the last timing event. + * Executes a given block of code if the specified duration has not passed yet since the last timing event. * Optionally resets the timer after execution. * - * @param time the time interval in milliseconds to check. + * @param duration the time interval to check. * @param reset whether to reset the timer after running the block. Defaults to `true`. - * @param block the code block to execute if the time has passed. + * @param block the code block to execute if the duration has not passed. */ - fun runIfNotPassed(time: Long, reset: Boolean = true, block: () -> Unit) = - timePassed(time).also { passed -> + fun runIfNotPassed(duration: Duration, reset: Boolean = true, block: () -> Unit) = + timePassed(duration).also { passed -> if (passed) return@also if (reset) reset() @@ -82,15 +84,15 @@ class SimpleTimer { } /** - * Executes a given block of code in safe context if the specified time has passed since the last timing event. + * Executes a given block of code in safe context if the specified duration has passed since the last timing event. * Optionally resets the timer after execution. * - * @param time the time interval in milliseconds to check. + * @param duration the time interval to check. * @param reset whether to reset the timer after running the block. Defaults to `true`. - * @param block the code block to execute if the time has passed. + * @param block the code block to execute if the duration has passed. */ - fun runSafeIfPassed(time: Long, reset: Boolean = true, block: SafeContext.() -> Unit) = - timePassed(time).also { passed -> + fun runSafeIfPassed(duration: Duration, reset: Boolean = true, block: SafeContext.() -> Unit) = + timePassed(duration).also { passed -> if (!passed) return@also runSafe { @@ -100,15 +102,15 @@ class SimpleTimer { } /** - * Executes a given block of code in safe context if the specified time has not passed yet since the last timing event. + * Executes a given block of code in safe context if the specified duration has not passed yet since the last timing event. * Optionally resets the timer after execution. * - * @param time the time interval in milliseconds to check. + * @param duration the time interval to check. * @param reset whether to reset the timer after running the block. Defaults to `true`. - * @param block the code block to execute if the time has passed. + * @param block the code block to execute if the duration has not passed. */ - fun runSafeIfNotPassed(time: Long, reset: Boolean = true, block: SafeContext.() -> Unit) = - timePassed(time).also { passed -> + fun runSafeIfNotPassed(duration: Duration, reset: Boolean = true, block: SafeContext.() -> Unit) = + timePassed(duration).also { passed -> if (passed) return@also runSafe { @@ -120,13 +122,9 @@ class SimpleTimer { /** * Resets the timer by updating the last timing event to the current time. * - * @param additionalDelay an additional delay in milliseconds to add to the current time. Defaults to `0`. + * @param additionalDelay an additional delay to add to the current time. Defaults to `Duration.ZERO`. */ - fun reset(additionalDelay: Long = 0L) { - lastTiming = currentTime + additionalDelay - } - - companion object { - private val currentTime get() = System.currentTimeMillis() + fun reset(additionalDelay: Duration = Duration.ZERO) { + lastTiming = TimeSource.Monotonic.markNow() + additionalDelay } } \ No newline at end of file From 478dd2001af6ee3f28d09ea31dafd1016e40ca0a Mon Sep 17 00:00:00 2001 From: blade Date: Sun, 2 Feb 2025 17:25:26 +0300 Subject: [PATCH 43/69] Request system, Rotation request rework, HotbarManager --- .../baritone/MixinBaritonePlayerContext.java | 2 +- .../mixin/baritone/MixinLookBehavior.java | 2 +- .../ClientPlayInteractionManagerMixin.java | 8 + .../mixin/entity/ClientPlayerEntityMixin.java | 4 +- .../com/lambda/mixin/entity/EntityMixin.java | 2 +- .../mixin/entity/LivingEntityMixin.java | 2 +- .../mixin/entity/PlayerEntityMixin.java | 2 +- .../mixin/entity/PlayerInventoryMixin.java | 47 +++ .../ClientPlayNetworkHandlerMixin.java | 7 +- .../com/lambda/mixin/render/CameraMixin.java | 2 +- .../render/LivingEntityRendererMixin.java | 2 +- .../lambda/config/groups/HotbarSettings.kt | 31 ++ .../lambda/config/groups/InteractionConfig.kt | 47 ++- .../config/groups/InteractionSettings.kt | 59 +++- .../lambda/config/groups/RotationSettings.kt | 23 +- .../com/lambda/config/groups/Targeting.kt | 6 +- .../com/lambda/event/events/InventoryEvent.kt | 38 ++- .../lambda/event/events/PlayerPacketEvent.kt | 2 +- .../com/lambda/event/events/RotationEvent.kt | 12 +- .../interaction/MovementConfiguration.kt | 2 +- .../lambda/interaction/PlayerPacketManager.kt | 3 +- .../interaction/blockplace/PlaceFinder.kt | 2 +- .../construction/blueprint/Blueprint.kt | 6 +- .../construction/context/BreakContext.kt | 2 +- .../construction/context/BuildContext.kt | 2 +- .../construction/context/PlaceContext.kt | 2 +- .../construction/simulation/BuildSimulator.kt | 112 ++++--- .../construction/simulation/Simulation.kt | 4 +- .../PickFromInventoryTransaction.kt | 2 +- .../lambda/interaction/request/Priority.kt | 20 ++ .../com/lambda/interaction/request/Request.kt | 24 ++ .../interaction/request/RequestConfig.kt | 27 ++ .../interaction/request/RequestHandler.kt | 69 +++++ .../request/hotbar/HotbarConfig.kt | 52 ++++ .../request/hotbar/HotbarManager.kt | 54 ++++ .../request/hotbar/HotbarRequest.kt | 33 +++ .../{ => request}/rotation/Rotation.kt | 8 +- .../request/rotation}/RotationConfig.kt | 34 ++- .../{ => request/rotation}/RotationManager.kt | 200 +++++-------- .../{ => request}/rotation/RotationMode.kt | 2 +- .../request/rotation/RotationRequest.kt | 55 ++++ .../rotation/visibilty/PointSelection.kt | 39 +++ .../rotation/visibilty/RequestedHit.kt | 112 +++++++ .../rotation/visibilty/RequestedHitDsl.kt | 45 +++ .../rotation/visibilty/RotationTarget.kt | 52 ++++ .../rotation/visibilty/RotationTargets.kt | 136 +++++++++ .../rotation}/visibilty/VisibilityChecker.kt | 171 +++++------ .../interaction/rotation/RotationRequest.kt | 38 --- .../lambda/module/modules/client/Baritone.kt | 2 +- .../module/modules/client/TaskFlowModule.kt | 20 +- .../lambda/module/modules/combat/Criticals.kt | 4 +- .../module/modules/combat/CrystalAura.kt | 277 ++++++++++-------- .../lambda/module/modules/combat/KillAura.kt | 86 ++---- .../lambda/module/modules/movement/Speed.kt | 51 ++-- .../module/modules/movement/TargetStrafe.kt | 2 +- .../lambda/module/modules/player/Freecam.kt | 26 +- .../module/modules/player/PacketMine.kt | 26 +- .../lambda/module/modules/player/Replay.kt | 16 +- .../lambda/module/modules/player/Scaffold.kt | 56 ++-- .../lambda/module/modules/render/Particles.kt | 6 +- .../com/lambda/task/tasks/BreakBlock.kt | 26 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 19 +- .../com/lambda/task/tasks/OpenContainer.kt | 22 +- .../com/lambda/task/tasks/PlaceBlock.kt | 32 +- .../lambda/util/collections/CachedValue.kt | 34 +++ .../lambda/util/collections/ResettableLazy.kt | 4 +- .../com/lambda/util/extension/Entity.kt | 4 +- .../kotlin/com/lambda/util/math/Linear.kt | 2 +- .../com/lambda/util/player/MovementUtils.kt | 2 +- .../player/prediction/PredictionEntity.kt | 2 +- .../util/player/prediction/PredictionTick.kt | 2 +- .../{RayCastMask.kt => InteractionMask.kt} | 2 +- .../lambda/util/world/raycast/RayCastUtils.kt | 6 +- .../src/main/resources/lambda.accesswidener | 1 + .../main/resources/lambda.mixins.common.json | 5 +- 75 files changed, 1571 insertions(+), 770 deletions(-) create mode 100644 common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java create mode 100644 common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/Priority.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/Request.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt rename common/src/main/kotlin/com/lambda/interaction/{ => request}/rotation/Rotation.kt (96%) rename common/src/main/kotlin/com/lambda/{config/groups => interaction/request/rotation}/RotationConfig.kt (58%) rename common/src/main/kotlin/com/lambda/interaction/{ => request/rotation}/RotationManager.kt (52%) rename common/src/main/kotlin/com/lambda/interaction/{ => request}/rotation/RotationMode.kt (95%) create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHitDsl.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt rename common/src/main/kotlin/com/lambda/interaction/{ => request/rotation}/visibilty/VisibilityChecker.kt (60%) delete mode 100644 common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt create mode 100644 common/src/main/kotlin/com/lambda/util/collections/CachedValue.kt rename common/src/main/kotlin/com/lambda/util/world/raycast/{RayCastMask.kt => InteractionMask.kt} (91%) diff --git a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java index f2f15593b..0c81c8144 100644 --- a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java +++ b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java @@ -20,7 +20,7 @@ import baritone.Baritone; import baritone.api.utils.Rotation; import baritone.utils.player.BaritonePlayerContext; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import com.lambda.util.BaritoneUtils; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; diff --git a/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java b/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java index 7688253a4..62246595b 100644 --- a/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java +++ b/common/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java @@ -21,7 +21,7 @@ import baritone.api.event.events.RotationMoveEvent; import baritone.api.utils.Rotation; import baritone.behavior.LookBehavior; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import com.lambda.util.BaritoneUtils; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java index bbf0ad18e..e856e9ff9 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java @@ -18,11 +18,13 @@ package com.lambda.mixin.entity; import com.lambda.event.EventFlow; +import com.lambda.event.events.InventoryEvent; import com.lambda.event.events.PlayerEvent; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerInteractionManager; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; import net.minecraft.screen.slot.SlotActionType; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; @@ -34,6 +36,7 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -83,6 +86,11 @@ public void clickSlotHead(int syncId, int slotId, int button, SlotActionType act if (EventFlow.post(click).isCanceled()) ci.cancel(); } + @Redirect(method = "syncSelectedSlot", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/PlayerInventory;selectedSlot:I")) + public int overrideSelectedSlotSync(PlayerInventory instance) { + return EventFlow.post(new InventoryEvent.HotbarSlot.Update(instance.selectedSlot)).getSlot(); + } + @Inject(method = "updateBlockBreakingProgress", at = @At("HEAD"), cancellable = true) private void updateBlockBreakingProgressPre(BlockPos pos, Direction side, CallbackInfoReturnable cir) { var event = EventFlow.post(new PlayerEvent.Breaking.Update(pos, side, currentBreakingProgress)); diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java index 9b0c3aad4..853362ca8 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -23,7 +23,7 @@ import com.lambda.event.events.PlayerEvent; import com.lambda.event.events.TickEvent; import com.lambda.interaction.PlayerPacketManager; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import net.minecraft.client.input.Input; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.MovementType; @@ -97,8 +97,6 @@ void sendBegin(CallbackInfo ci) { ci.cancel(); PlayerPacketManager.sendPlayerPackets(); autoJumpEnabled = Lambda.getMc().options.getAutoJump().getValue(); - - RotationManager.update(); } @Inject(method = "tick", at = @At(value = "HEAD")) diff --git a/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java index e5732ef2d..8cdbe3c8d 100644 --- a/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java @@ -21,7 +21,7 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.EntityEvent; import com.lambda.event.events.PlayerEvent; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import com.lambda.util.math.Vec2d; import net.minecraft.entity.Entity; import net.minecraft.entity.MovementType; diff --git a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java index 60d805113..02354fa3a 100644 --- a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java @@ -20,7 +20,7 @@ import com.lambda.Lambda; import com.lambda.event.EventFlow; import com.lambda.event.events.MovementEvent; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import net.minecraft.entity.LivingEntity; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; diff --git a/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java index e4aca2cdc..a9a3dd519 100644 --- a/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java @@ -19,7 +19,7 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.MovementEvent; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import net.minecraft.client.MinecraftClient; import net.minecraft.entity.player.PlayerEntity; import org.spongepowered.asm.mixin.Mixin; diff --git a/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java b/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java new file mode 100644 index 000000000..6c42a71db --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.mixin.entity; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import static net.minecraft.entity.player.PlayerInventory.isValidHotbarIndex; + +@Mixin(PlayerInventory.class) +public class PlayerInventoryMixin { + @Inject(method = "getMainHandStack", at = @At(value = "HEAD"), cancellable = true) + public void handleSpoofedMainHandStack(CallbackInfoReturnable cir) { + PlayerInventory instance = (PlayerInventory) (Object) this; + MinecraftClient mc = MinecraftClient.getInstance(); + ClientPlayerInteractionManager interaction = mc.interactionManager; + + if (instance.player != mc.player || interaction == null) return; + + int actualSlot = interaction.lastSelectedSlot; + + cir.setReturnValue( + isValidHotbarIndex(actualSlot) ? instance.main.get(actualSlot) : ItemStack.EMPTY + ); + } +} diff --git a/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java b/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java index 12267421c..3db1ca635 100644 --- a/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java +++ b/common/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java @@ -19,22 +19,19 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.InventoryEvent; -import com.lambda.event.events.WorldEvent; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket; -import net.minecraft.world.explosion.Explosion; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ClientPlayNetworkHandler.class) public class ClientPlayNetworkHandlerMixin { - @Inject(method = "onUpdateSelectedSlot", at = @At("TAIL")) + @Inject(method = "onUpdateSelectedSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V", shift = At.Shift.AFTER), cancellable = true) private void onUpdateSelectedSlot(UpdateSelectedSlotS2CPacket packet, CallbackInfo ci) { - EventFlow.post(new InventoryEvent.SelectedHotbarSlotUpdate(packet.getSlot())); + if (EventFlow.post(new InventoryEvent.HotbarSlot.Sync(packet.getSlot())).isCanceled()) ci.cancel(); } @Inject(method = "onScreenHandlerSlotUpdate", at = @At("TAIL")) diff --git a/common/src/main/java/com/lambda/mixin/render/CameraMixin.java b/common/src/main/java/com/lambda/mixin/render/CameraMixin.java index 8aa9ecb58..58269ecad 100644 --- a/common/src/main/java/com/lambda/mixin/render/CameraMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/CameraMixin.java @@ -17,7 +17,7 @@ package com.lambda.mixin.render; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import com.lambda.module.modules.player.Freecam; import com.lambda.module.modules.render.CameraTweaks; import net.minecraft.client.render.Camera; diff --git a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java index f77f55ebe..e79400612 100644 --- a/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java @@ -18,7 +18,7 @@ package com.lambda.mixin.render; import com.lambda.Lambda; -import com.lambda.interaction.RotationManager; +import com.lambda.interaction.request.rotation.RotationManager; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.LivingEntityRenderer; import net.minecraft.client.util.math.MatrixStack; diff --git a/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt new file mode 100644 index 000000000..5d7391bcd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.config.groups + +import com.lambda.config.Configurable +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.hotbar.HotbarConfig + +class HotbarSettings( + c: Configurable, + priority: Priority = 0, + vis: () -> Boolean = { true } +) : HotbarConfig(priority) { + override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", vis) + override var switchPause by c.setting("Switch Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt index d4ed1337c..92b260fcc 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt @@ -17,33 +17,50 @@ package com.lambda.config.groups -import com.lambda.core.PingManager +import com.lambda.interaction.request.rotation.visibilty.PointSelection interface InteractionConfig { /** - * Minecraft's default reach values + * Maximum entity interaction distance */ - val defaultReach: Boolean + val attackReach: Double /** - * Maximum distance to interact. + * Maximum block interaction distance */ - val reach: Double + val placeReach: Double - val visibilityCheck: Boolean + /** + * Maximum possible interaction distance + * + * Equals to `max(attackReach, placeReach)` if both are present. Equals to one of them otherwise + */ + val scanReach: Double + + /** + * Whether to include the environment to the ray cast context. + * + * if false: skips walls for entities, skips entities for blocks. + */ + val strictRayCast: Boolean + + /** + * Whether to check if an AABB side is visible. + */ + val checkSideVisibility: Boolean /** - * Will check `resolution squared` many points on a grid on each visible surface of the hit box. + * Grid divisions count per surface of the hit box. */ val resolution: Int - val useRayCast: Boolean - val swingHand: Boolean - val inScopeThreshold: Int - val pingTimeout: Boolean + /** + * The way to select the best point. + */ + val pointSelection: PointSelection - val scopeThreshold: Int - get() = - if (pingTimeout) PingManager.lastPing.toInt() / 50 - else inScopeThreshold + /** + * Whether to swing the hand when interacting. + */ + val swingHand: Boolean } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index ff567e318..389573b81 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -18,26 +18,53 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.threading.runSafe -import net.minecraft.entity.player.PlayerEntity +import com.lambda.interaction.request.rotation.visibilty.PointSelection +import com.lambda.util.world.raycast.InteractionMask +import kotlin.math.max -// TODO: Rewrite the group settings plz class InteractionSettings( c: Configurable, - useDefaultReach: Boolean = true, - cReach: Double = 3.0, + private val usage: InteractionMask, vis: () -> Boolean = { true }, ) : InteractionConfig { - override val defaultReach by c.setting("Default Reach", useDefaultReach) - private val customReach by c.setting("Reach", if (!defaultReach) cReach else 4.5, 0.1..10.0, 0.1, "Players reach / range", " blocks") { vis() && !defaultReach } - override val reach: Double - get() = if (defaultReach) runSafe { PlayerEntity.getReachDistance(interaction.currentGameMode.isCreative).toDouble() } ?: 4.5 else customReach - override val useRayCast by c.setting("Raycast", true, "Verify hit vector with ray casting (for very strict ACs)", vis) - override val visibilityCheck by c.setting("Visibility Check", true, "Check if target is visible", vis) - override val resolution by c.setting("Resolution", 4, 1..40, 1, "How many raycast checks per surface (will be squared)") { vis() && useRayCast } - override val swingHand by c.setting("Swing Hand", true, "Swing hand on interactions", vis) - override val pingTimeout by c.setting("Ping Timeout", false, "Timeout on high ping", vis) - override val inScopeThreshold by c.setting("Constant Timeout", 1, 0..20, 1, "How many ticks to wait after target box is in rotation scope"," ticks") { - vis() && !pingTimeout + // Reach + private val useDefaultReach by c.setting("Default Reach", true, "Whether to use vanilla interaction ranges", vis) + private val attackReachSetting = if (usage.entity) c.setting("Attack Reach", DEFAULT_ATTACK_REACH, 1.0..10.0, 0.01, "Maximum entity interaction distance") { vis() && !useDefaultReach } else null + private val placeReachSetting = if (usage.block) c.setting("Place Reach", DEFAULT_PLACE_REACH, 1.0..10.0, 0.01, "Maximum block interaction distance") { vis() && !useDefaultReach } else null + + override val attackReach: Double get() { + check(usage.entity) { + "Given interaction config has no attack reach implementation" + } + + return if (useDefaultReach) DEFAULT_ATTACK_REACH else attackReachSetting!!.value + } + + override val placeReach: Double get() { + check(usage.block) { + "Given interaction config has no place reach implementation" + } + + return if (useDefaultReach) DEFAULT_ATTACK_REACH else placeReachSetting!!.value + } + + override val scanReach: Double get() = when (usage) { + InteractionMask.ENTITY -> attackReach + InteractionMask.BLOCK -> placeReach + InteractionMask.BOTH -> max(attackReach, placeReach) + } + + // Point scan + override val strictRayCast by c.setting("Strict Raycast", true, "Whether to include the environment to the ray cast context", vis) + override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible", vis) + override val resolution by c.setting("Resolution", 8, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", vis) + override val pointSelection by c.setting("Point Selection", PointSelection.ByRotation, "The way to select the best point", vis) + + // Swing + override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", vis) + + companion object { + const val DEFAULT_ATTACK_REACH = 3.0 + const val DEFAULT_PLACE_REACH = 4.5 } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 0c1998847..c9cc188d7 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -18,21 +18,26 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.interaction.rotation.RotationMode +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotation.RotationMode +import com.lambda.interaction.request.rotation.RotationRequest import kotlin.math.* import kotlin.random.Random class RotationSettings( c: Configurable, - vis: () -> Boolean = { true }, -) : RotationConfig { + priority: Priority = 0, + vis: () -> Boolean = { true } +) : RotationConfig(priority) { override var rotationMode by c.setting("Mode", RotationMode.SYNC, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", vis) /** How many ticks to keep the rotation before resetting */ override val keepTicks by c.setting("Keep Rotation", 3, 0..10, 1, "Ticks to keep rotation", " ticks") { rotate && vis() } /** How many ticks to wait before resetting the rotation */ - override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks") { rotate && vis() } + override val decayTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks") { rotate && vis() } /** Whether the rotation is instant */ var instant by c.setting("Instant Rotation", true, "Instantly rotate") { rotate && vis() } @@ -41,23 +46,21 @@ class RotationSettings( * The mean (average/base) value used to calculate rotation speed. * This value represents the center of the distribution. */ - private var mean by c.setting("Mean", 40.0, 1.0..120.0, 0.1, "Average rotation speed", unit = "°") { rotate && vis() && !instant } + var mean by c.setting("Mean", 40.0, 1.0..120.0, 0.1, "Average rotation speed", unit = "°") { rotate && vis() && !instant } /** * The standard deviation for the Gaussian distribution used to calculate rotation speed. * This value represents the spread of rotation speed. */ - private var spread by c.setting("Spread", 10.0, 0.0..60.0, 0.1, "Spread of rotation speeds", unit = "°") { rotate && vis() && !instant } + var spread by c.setting("Spread", 10.0, 0.0..60.0, 0.1, "Spread of rotation speeds", unit = "°") { rotate && vis() && !instant } /** * We must always provide turn speed to the interpolator because the player's yaw might exceed the -180 to 180 range. * Therefore, we cannot simply assign new angles to the player's rotation without getting flagged by Grim's AimModulo360 check. */ - override val turnSpeed get() = if (instant) 180.0 else abs(mean + spread * nextRandom()) + override val turnSpeed get() = if (instant) 180.0 else abs(mean + spread * nextGaussian()) - var speedMultiplier = 1.0 - - private fun nextRandom(): Double { + private fun nextGaussian(): Double { val u1 = Random.nextDouble() val u2 = Random.nextDouble() diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index d6435e5b4..8fe0c4bde 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -20,9 +20,9 @@ package com.lambda.config.groups import com.lambda.config.Configurable import com.lambda.context.SafeContext import com.lambda.friend.FriendManager.isFriend -import com.lambda.interaction.rotation.Rotation.Companion.dist -import com.lambda.interaction.rotation.Rotation.Companion.rotation -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.Rotation.Companion.dist +import com.lambda.interaction.request.rotation.Rotation.Companion.rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.threading.runSafe import com.lambda.util.math.distSq import com.lambda.util.world.fastEntitySearch diff --git a/common/src/main/kotlin/com/lambda/event/events/InventoryEvent.kt b/common/src/main/kotlin/com/lambda/event/events/InventoryEvent.kt index 026998137..8c4299c90 100644 --- a/common/src/main/kotlin/com/lambda/event/events/InventoryEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/InventoryEvent.kt @@ -18,6 +18,9 @@ package com.lambda.event.events import com.lambda.event.Event +import com.lambda.event.callback.Cancellable +import com.lambda.event.callback.ICancellable +import com.lambda.interaction.request.hotbar.HotbarRequest import net.minecraft.item.ItemStack import net.minecraft.screen.ScreenHandler @@ -80,10 +83,33 @@ sealed class InventoryEvent { val stack: ItemStack, ) : Event - /** - * Represents an event triggered when a player switches their selected hotbar slot. - * - * @property slot The index of the newly selected hotbar slot. - */ - data class SelectedHotbarSlotUpdate(val slot: Int) : Event + abstract class HotbarSlot: Event { + + data class Request(var request: HotbarRequest? = null) : HotbarSlot() + + /** + * Represents an event triggered when the client attempts to send slot update to the server. + * + * Updated slot id will come to the server if it defers from last reported slot. + */ + data class Update(var slot: Int) : HotbarSlot() + + /** + * Represents an event triggered when the client sends slot update to the server. + * + * This event happens when last slot defers from the previous one + */ + data class Changed(var slot: Int) : HotbarSlot() + + /** + * Represents an event triggered when the server forces the player to change active hotbar slot. + * (world load or anticheat flag). + * + * This event is [Cancellable], you may ignore the server with risk + * of unexpected behaviour depending on the strictness of the server/anticheat. + * + * @property slot The index of the newly selected hotbar slot. + */ + data class Sync(val slot: Int) : HotbarSlot(), ICancellable by Cancellable() + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt b/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt index 6bccf9eea..6025f04ab 100644 --- a/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt @@ -20,7 +20,7 @@ package com.lambda.event.events import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket import net.minecraft.util.math.Vec3d diff --git a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt index 708c231ed..6fe769595 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -20,20 +20,10 @@ package com.lambda.event.events import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable -import com.lambda.interaction.rotation.RotationRequest +import com.lambda.interaction.request.rotation.RotationRequest import net.minecraft.client.input.Input sealed class RotationEvent { - /** - * This event allows listeners to register a rotation request to be executed that tick. - * - * Only one rotation can "win" each tick - * - * CAUTION: The listener with the LOWEST priority will win as it is the last to override the context - * - * @property request The rotation context that listeners can set. Only one rotation can "win" each tick - */ - data class Update(var request: RotationRequest?) : ICancellable by Cancellable() /** * This event allows listeners to modify the yaw relative to which the movement input is going to be constructed diff --git a/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt b/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt index 0d741bb35..4974998c4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt +++ b/common/src/main/kotlin/com/lambda/interaction/MovementConfiguration.kt @@ -17,7 +17,7 @@ package com.lambda.interaction -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation import net.minecraft.util.math.Vec3d data class MovementConfiguration( diff --git a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index 6dc00a86f..11ccdbed8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -21,7 +21,8 @@ import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.EventFlow.postChecked import com.lambda.event.events.PlayerPacketEvent -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.RotationManager import com.lambda.threading.runSafe import com.lambda.util.collections.LimitedOrderedSet import com.lambda.util.math.approximate diff --git a/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt b/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt index fbcfd5297..48bca00e2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt +++ b/common/src/main/kotlin/com/lambda/interaction/blockplace/PlaceFinder.kt @@ -20,7 +20,7 @@ package com.lambda.interaction.blockplace import com.lambda.context.SafeContext import com.lambda.interaction.blockplace.PlaceInteraction.canPlaceAt import com.lambda.interaction.blockplace.PlaceInteraction.isClickable -import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.util.BlockUtils.blockState import com.lambda.util.math.distSq import com.lambda.util.math.getHitVec diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt index 693b3c35e..9007c5efc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt @@ -19,7 +19,7 @@ package com.lambda.interaction.construction.blueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.BlockUtils.blockPos -import com.lambda.util.collections.ResettableLazy +import com.lambda.util.collections.resettableLazy import com.lambda.util.extension.Structure import com.lambda.util.math.roundedBlockPos import net.minecraft.structure.StructureTemplate @@ -28,8 +28,8 @@ import net.minecraft.util.math.* abstract class Blueprint { abstract val structure: Structure - val bounds = ResettableLazy { - if (structure.isEmpty()) return@ResettableLazy null + val bounds = resettableLazy { + if (structure.isEmpty()) return@resettableLazy null val maxX = structure.keys.maxOf { it.x } val maxY = structure.keys.maxOf { it.y } val maxZ = structure.keys.maxOf { it.z } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 682e7585f..21fd2900a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -18,7 +18,7 @@ package com.lambda.interaction.construction.context import com.lambda.context.SafeContext -import com.lambda.interaction.rotation.RotationRequest +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.util.Hand diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 6065dfc6a..b62e78b53 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -18,7 +18,7 @@ package com.lambda.interaction.construction.context import com.lambda.interaction.construction.result.Drawable -import com.lambda.interaction.rotation.RotationRequest +import com.lambda.interaction.request.rotation.RotationRequest import net.minecraft.block.BlockState import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 8f24285b7..f30147e52 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -21,7 +21,7 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.rotation.RotationRequest +import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils import com.lambda.util.Communication.warn diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 270b395d1..1ba80903d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -18,7 +18,7 @@ package com.lambda.interaction.construction.simulation import com.lambda.context.SafeContext -import com.lambda.interaction.RotationManager +import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.PlaceContext @@ -28,13 +28,14 @@ import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.container.ContainerManager.findBestAvailableTool -import com.lambda.interaction.rotation.Rotation.Companion.rotation -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces -import com.lambda.interaction.visibilty.VisibilityChecker.optimum -import com.lambda.interaction.visibilty.VisibilityChecker.scanSurfaces -import com.lambda.interaction.visibilty.VisibilityChecker.visibleSides +import com.lambda.interaction.request.rotation.Rotation.Companion.rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.visibilty.RequestedHit +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces +import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -54,7 +55,6 @@ import net.minecraft.item.ItemUsageContext import net.minecraft.registry.RegistryKeys import net.minecraft.state.property.Properties import net.minecraft.util.Hand -import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.HitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box @@ -64,7 +64,7 @@ import kotlin.jvm.optionals.getOrNull import kotlin.math.pow object BuildSimulator { - fun Blueprint.simulate(eye: Vec3d, reach: Double = TaskFlowModule.interact.reach) = + fun Blueprint.simulate(eye: Vec3d, reach: Double = TaskFlowModule.interact.placeReach) = runSafe { structure.entries.flatMap { (pos, target) -> checkRequirements(pos, target)?.let { @@ -148,41 +148,38 @@ object BuildSimulator { if (voxelShape.isEmpty) return@forEach val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } - val verify: HitResult.() -> Boolean = { - blockResult?.blockPos == hitPos && blockResult?.side == hitSide + val verify: CheckedHit.() -> Boolean = { + hit.blockResult?.blockPos == hitPos && hit.blockResult?.side == hitSide } - val validHits = mutableMapOf() + + val checkedHits = mutableListOf() val misses = mutableSetOf() val reachSq = reach.pow(2) boxes.forEach { box -> - val res = if (TaskFlowModule.interact.useRayCast) interact.resolution else 4 - val sides = if (TaskFlowModule.interact.visibilityCheck) { + val sides = if (interact.checkSideVisibility) { box.getVisibleSurfaces(eye).intersect(setOf(hitSide)) } else { Direction.entries.toSet() } - scanSurfaces(box, sides, res, preprocessing.surfaceScan) { side, vec -> + scanSurfaces(box, sides, interact.resolution, preprocessing.surfaceScan) { _, vec -> if (eye distSq vec > reachSq) { misses.add(vec) return@scanSurfaces } - validHits[vec] = - if (TaskFlowModule.interact.useRayCast && TaskFlowModule.interact.visibilityCheck) { - val cast = eye.rotationTo(vec) - .rayCast(reach, eye) ?: return@scanSurfaces - if (!cast.verify()) return@scanSurfaces + val newRotation = eye.rotationTo(vec) + + val hit = newRotation.rayCast(reach, eye) ?: return@scanSurfaces + val checked = CheckedHit(hit, newRotation, reach) + if (!checked.verify()) return@scanSurfaces - cast - } else { - BlockHitResult(vec, side, hitPos, false) - } + checkedHits.add(checked) } } - if (validHits.isEmpty()) { + if (checkedHits.isEmpty()) { if (misses.isNotEmpty()) { acc.add(BuildResult.OutOfReach(pos, eye, misses)) return@forEach @@ -192,24 +189,21 @@ object BuildSimulator { return@forEach } - validHits.keys.optimum?.let { optimum -> - validHits.minByOrNull { optimum distSq it.key }?.let { closest -> - val optimumRotation = eye.rotationTo(closest.key) - RotationRequest(optimumRotation, rotation, closest.value, verify) - } - }?.let { rotation -> + interact.pointSelection.select(checkedHits)?.let { checkedHit -> val optimalStack = target.getStack(world, pos) // ToDo: For each hand and sneak or not? val fakePlayer = copyPlayer(player).apply { setPos(eye.x, eye.y - standingEyeHeight, eye.z) - this.rotation = rotation.rotation + this.rotation = checkedHit.targetRotation } + val checkedResult = checkedHit.hit + val usageContext = ItemUsageContext( fakePlayer, Hand.MAIN_HAND, - rotation.checkedResult?.blockResult, + checkedResult.blockResult, ) val cachePos = CachedBlockPosition( usageContext.world, @@ -269,7 +263,7 @@ object BuildSimulator { return@forEach } - val blockHit = rotation.checkedResult?.blockResult ?: return@forEach + val blockHit = checkedResult.blockResult ?: return@forEach val hitBlock = blockHit.blockPos.blockState(world).block val shouldSneak = hitBlock in BlockUtils.interactionBlacklist @@ -281,7 +275,10 @@ object BuildSimulator { val placeContext = PlaceContext( eye, blockHit, - rotation, + RotationRequest( + lookAtBlock(blockHit.blockPos, setOf(blockHit.side), interact), + rotation + ), eye.distanceTo(blockHit.pos), resultState, blockHit.blockPos.blockState(world), @@ -378,11 +375,18 @@ object BuildSimulator { } val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } - val verify: HitResult.() -> Boolean = { blockResult?.blockPos == pos } + val verify: CheckedHit.() -> Boolean = { + hit.blockResult?.blockPos == pos + } + + val checkedHits = mutableListOf() /* the player is buried inside the block */ if (boxes.any { it.contains(eye) }) { currentCast?.blockResult?.let { blockHit -> - val rotationRequest = RotationRequest(currentRotation, rotation, currentCast, verify) + val rotationRequest = RotationRequest( + lookAtBlock(pos, interactionConfig = interact), + rotation + ) val breakContext = BreakContext( eye, blockHit, @@ -401,23 +405,19 @@ object BuildSimulator { val reachSq = reach.pow(2) boxes.forEach { box -> - val res = if (TaskFlowModule.interact.useRayCast) interact.resolution else 2 - val sides = visibleSides(box, eye, TaskFlowModule.interact) - scanSurfaces(box, sides, res) { side, vec -> + scanSurfaces(box, Direction.entries.toSet(), interact.resolution) { side, vec -> if (eye distSq vec > reachSq) { misses.add(vec) return@scanSurfaces } - validHits[vec] = if (TaskFlowModule.interact.useRayCast && TaskFlowModule.interact.visibilityCheck) { - val cast = eye.rotationTo(vec) - .rayCast(reach, eye) ?: return@scanSurfaces - if (!cast.verify()) return@scanSurfaces + val newRotation = eye.rotationTo(vec) - cast - } else { - BlockHitResult(vec, side, pos, false) - } + val hit = newRotation.rayCast(reach, eye) ?: return@scanSurfaces + val checked = CheckedHit(hit, newRotation, reach) + if (!checked.verify()) return@scanSurfaces + + checkedHits.add(checked) } } @@ -427,18 +427,16 @@ object BuildSimulator { return acc } - validHits.keys.optimum?.let { optimum -> - validHits.minByOrNull { optimum distSq it.key }?.let { closest -> - val optimumRotation = eye.rotationTo(closest.key) - RotationRequest(optimumRotation, rotation, closest.value, verify) - } - }?.let { bestRotation -> - val blockHit = bestRotation.checkedResult?.blockResult ?: return@let + interact.pointSelection.select(checkedHits)?.let { checkedHit -> + val blockHit = checkedHit.hit.blockResult ?: return@let val breakContext = BreakContext( eye, blockHit, - bestRotation, + RotationRequest( + lookAtBlock(blockHit.blockPos, interactionConfig = interact), + rotation + ), state, player.activeHand, instantBreakable(state, pos) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index bcab47775..b6e7dcfdd 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -49,7 +49,9 @@ data class Simulation(val blueprint: Blueprint) { .isSideSolidFullSquare(world, blockPos, Direction.UP) ) return@getOrPut emptySet() } - blueprint.simulate(view, reach = TaskFlowModule.interact.reach - 1) + + // ToDo: wtf was that "reach = TaskFlowModule.interact.reach - 1" + blueprint.simulate(view) } private fun SafeContext.playerFitsIn(pos: Vec3d): Boolean { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt index 9197741fe..eb592261f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt @@ -36,7 +36,7 @@ class PickFromInventoryTransaction @Ta5kBuilder constructor( confirming = true } - listen { + listen { finish() } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Priority.kt b/common/src/main/kotlin/com/lambda/interaction/request/Priority.kt new file mode 100644 index 000000000..26d6f21e7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/Priority.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request + +typealias Priority = Int \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/Request.kt b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt new file mode 100644 index 000000000..49e253ad2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/Request.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request + +abstract class Request ( + val priority: Priority +) { + abstract val done: Boolean +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt new file mode 100644 index 000000000..ee906c748 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestConfig.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request + +abstract class RequestConfig ( + val priority: Priority +) { + protected abstract fun requestInternal(request: R) + + fun request(request: R): R = + request.apply(::requestInternal) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt new file mode 100644 index 000000000..15ab9caf6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request + +import java.util.concurrent.ConcurrentHashMap + +/** + * This class manages a collection of requests, each associated with a `RequestConfig`. + * It provides a mechanism to register requests and select the highest priority request + * for processing. + */ +abstract class RequestHandler { + + private val requestMap = ConcurrentHashMap, T>() + + /** + * The currently active request. + */ + var currentRequest: T? = null; protected set + + /** + * Registers a new request with the given configuration. + * + * @param config The configuration for the request. + * @param request The request to register. + * @return The registered request. + */ + fun registerRequest(config: RequestConfig, request: T): T { + requestMap[config] = request + return request + } + + /** + * Updates the current request to the highest priority registered request. + * Clears the internal request map after updating. + * + * @return True, if the request was updated. + */ + protected fun updateRequest( + keepIfNull: Boolean = false, + filter: (Map.Entry, T>) -> Boolean = { true } + ): Boolean { + val prev = currentRequest + + currentRequest = requestMap.entries + .filter(filter) + .maxByOrNull { it.key.priority }?.value + + if (keepIfNull && currentRequest == null) currentRequest = prev + + requestMap.clear() + return prev != currentRequest + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt new file mode 100644 index 000000000..140ec6d0f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.hotbar + +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.RequestConfig + +/* + * Abstract base class for configuring hotbar slot switch behavior. + * + * @param priority The priority of this configuration. + */ +abstract class HotbarConfig( + priority: Priority +) : RequestConfig(priority) { + + /** + * The number of ticks to keep the current hotbar selection active. + */ + abstract val keepTicks: Int + + /** + * The delay in ticks to pause actions after switching to the slot. + * + * Affects the validity state of the request + */ + abstract var switchPause: Int + + /** + * Registers a hotbar request with the HotbarManager. + * + * @param request The hotbar request to register. + */ + override fun requestInternal(request: HotbarRequest) { + HotbarManager.registerRequest(this, request) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt new file mode 100644 index 000000000..ed8419418 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.hotbar + +import com.lambda.core.Loadable +import com.lambda.event.events.InventoryEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.RequestHandler +import com.lambda.threading.runSafe + +object HotbarManager : RequestHandler(), Loadable { + val serverSlot get() = runSafe { + interaction.lastSelectedSlot + } ?: -1 + + override fun load() = "Loaded Hotbar Manager" + + init { + listen { + it.slot = currentRequest?.slot ?: return@listen + } + + listen { + updateRequest() + } + + listen { + val request = currentRequest ?: return@listen + + request.keepTicks-- + request.switchPause-- + + if (request.keepTicks <= 0) { + currentRequest = null + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt new file mode 100644 index 000000000..dc297aeee --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.hotbar + +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.Request + +class HotbarRequest( + val slot: Int, + priority: Priority, + var keepTicks: Int = 3, + var switchPause: Int = 0, +) : Request(priority) { + override val done: Boolean get() = + // The request has to be valid at least for 1 tick + // (if for some dumb reason the switch pause is bigger than the decay time) + HotbarManager.serverSlot == slot && (switchPause <= 0 || keepTicks <= 0) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt similarity index 96% rename from common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt index 9288eee45..eadaf72e8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/Rotation.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.rotation +package com.lambda.interaction.request.rotation import com.lambda.Lambda.mc import com.lambda.threading.runSafe @@ -24,7 +24,7 @@ import com.lambda.util.math.MathUtils.toRadian import com.lambda.util.math.Vec2d import com.lambda.util.math.plus import com.lambda.util.math.times -import com.lambda.util.world.raycast.RayCastMask +import com.lambda.util.world.raycast.InteractionMask import com.lambda.util.world.raycast.RayCastUtils.rayCast import net.minecraft.entity.Entity import net.minecraft.util.math.Box @@ -62,7 +62,7 @@ data class Rotation(val yaw: Double, val pitch: Double) { reach: Double, eye: Vec3d? = null, fluids: Boolean = false, - mask: RayCastMask = RayCastMask.BOTH, + mask: InteractionMask = InteractionMask.BOTH, ) = runSafe { rayCast(eye ?: player.eyePos, vector, reach, mask, fluids) } diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt similarity index 58% rename from common/src/main/kotlin/com/lambda/config/groups/RotationConfig.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt index 4a65e3da4..7c6be4db3 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,38 +15,50 @@ * along with this program. If not, see . */ -package com.lambda.config.groups +package com.lambda.interaction.request.rotation -import com.lambda.interaction.rotation.RotationMode +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.RequestConfig +import com.lambda.interaction.request.rotation.visibilty.RotationTarget -interface RotationConfig { +/* + * Abstract base class for configuring rotation behavior. + * + * @param priority The priority of this configuration. + */ +abstract class RotationConfig(priority: Priority) : RequestConfig(priority) { /** * - [RotationMode.SILENT] Spoofing server-side rotation. * - [RotationMode.SYNC] Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). * - [RotationMode.LOCK] Locks the camera client-side. */ - val rotationMode: RotationMode + abstract val rotationMode: RotationMode /** * The rotation speed (in degrees). */ - val turnSpeed: Double + abstract val turnSpeed: Double /** * Ticks the rotation should not be changed. */ - val keepTicks: Int + abstract val keepTicks: Int /** * Ticks to rotate back to the actual rotation. */ - val resetTicks: Int + abstract val decayTicks: Int val rotate: Boolean get() = rotationMode != RotationMode.NONE - interface Instant : RotationConfig { + override fun requestInternal(request: RotationRequest) { + RotationManager.registerRequest(this, request) + } + + open class Instant(mode: RotationMode, priority: Priority = 0) : RotationConfig(priority) { override val turnSpeed get() = 360.0 override val keepTicks get() = 1 - override val resetTicks get() = 1 + override val decayTicks get() = 1 + override val rotationMode = mode } -} +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt similarity index 52% rename from common/src/main/kotlin/com/lambda/interaction/RotationManager.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index e0ea84cbb..f1aee669c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,24 +15,22 @@ * along with this program. If not, see . */ -package com.lambda.interaction +package com.lambda.interaction.request.rotation -import com.lambda.Lambda.mc -import com.lambda.config.groups.RotationSettings +import com.lambda.Lambda import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.PacketEvent +import com.lambda.event.events.PlayerPacketEvent import com.lambda.event.events.RotationEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe -import com.lambda.interaction.rotation.Rotation -import com.lambda.interaction.rotation.Rotation.Companion.angleDifference -import com.lambda.interaction.rotation.Rotation.Companion.fixSensitivity -import com.lambda.interaction.rotation.Rotation.Companion.slerp -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.rotation.RotationMode +import com.lambda.interaction.request.RequestHandler +import com.lambda.interaction.request.rotation.Rotation.Companion.fixSensitivity +import com.lambda.interaction.request.rotation.Rotation.Companion.slerp +import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.modules.client.Baritone import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafe @@ -48,73 +46,72 @@ import kotlin.math.round import kotlin.math.sign import kotlin.math.sin -object RotationManager : Loadable { +object RotationManager : RequestHandler(), Loadable { var currentRotation = Rotation.ZERO private var prevRotation = Rotation.ZERO - var currentContext: RotationRequest? = null - - private var keepTicks = 0 - private var pauseTicks = 0 - override fun load() = "Loaded Rotation Manager" - @RotationDsl - fun Any.rotate( - priority: Int = 0, + /** + * Registers a listener that is called right before [RotationManager] updates the context and the rotation + * + * Use this if you need to match specific state of the client + * (when the player and its input are already updated this tick and similar) / + * when you don't know when to rotate / you simply want to request before rotation update + */ + fun Any.onRotate( alwaysListen: Boolean = false, - block: RequestRotationBuilder.() -> Unit, - ) { - val builder = RequestRotationBuilder().apply(block) - var requested: RotationRequest? = null - - listen(priority, alwaysListen) { event -> - builder.onUpdate?.invoke(this, event.request)?.let { context -> - if (!context.config.rotate) return@let - event.request = context - requested = context + block: SafeContext.() -> Unit, + ) = this.listen(0, alwaysListen) { + block() + } + + init { + // For some reason we have to update AFTER sending player packets + // instead of updating on TickEvent.Pre (am I doing something wrong?) + listen(Int.MIN_VALUE) { + // Update the request + val changed = updateRequest(true) { entry -> + // skip requests that have failed to build the rotation + // to free the request place for others + entry.value.target.targetRotation.value != null } - } - listen { event -> - val context = event.request - if (!context.config.rotate) return@listen - if (context != requested) return@listen - if (!context.isValid) return@listen - builder.onReceive?.invoke(this, context) - } - } + if (!changed) { // rebuild the rotation if the same context gets used again + currentRequest?.target?.targetRotation?.update() + } - @DslMarker - annotation class RotationDsl + // Calculate the target rotation + val targetRotation = currentRequest?.let { request -> + val rotationTo = if (request.keepTicks >= 0) + request.target.targetRotation.value + ?: currentRotation // same context gets used again && the rotation is null this tick + else player.rotation - class RequestRotationBuilder { - var onUpdate: (SafeContext.(lastContext: RotationRequest?) -> RotationRequest?)? = null - var onReceive: (SafeContext.(context: RotationRequest) -> Unit)? = null + val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier + val turnSpeed = request.turnSpeed() * speedMultiplier - @RotationDsl - fun request(block: SafeContext.(lastContext: RotationRequest?) -> RotationRequest?) { - onUpdate = block - } + currentRotation.slerp(rotationTo, turnSpeed) + } ?: player.rotation - @RotationDsl - fun finished(block: SafeContext.(context: RotationRequest) -> Unit) { - onReceive = block - } - } + // Update the current rotation + prevRotation = currentRotation + currentRotation = targetRotation.fixSensitivity(prevRotation) - @JvmStatic - fun update() = runSafe { - RotationEvent.Update(BaritoneProcessor.poolContext()).post { - rotate(request) + // Handle LOCK mode + if (currentRequest?.mode == RotationMode.LOCK) { + player.yaw = currentRotation.yawF + player.pitch = currentRotation.pitchF + } - currentContext?.let { - RotationEvent.Post(it).post() + // Tick and reset the context + currentRequest?.let { + if (--it.keepTicks > 0) return@let + if (--it.decayTicks >= 0) return@let + currentRequest = null } } - } - init { listen { event -> val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listen @@ -124,101 +121,63 @@ object RotationManager : Loadable { } } - listenUnsafe { + listenUnsafe { reset(Rotation.ZERO) } } - private fun rotate(newContext: RotationRequest?) = runSafe { - prevRotation = currentRotation - - keepTicks-- - pauseTicks-- - - currentContext?.let { current -> - if (keepTicks + current.config.resetTicks < 0 || pauseTicks >= 0) { - currentContext = null - } - } - - newContext?.let { request -> - currentContext = request - keepTicks = request.config.keepTicks - } - - currentRotation = currentContext?.let { context -> - val rotationTo = if (keepTicks >= 0) context.rotation else player.rotation - - var speedMultiplier = (context.config as? RotationSettings)?.speedMultiplier ?: 1.0 - if (keepTicks < 0) speedMultiplier = 1.0 - - val turnSpeed = context.config.turnSpeed * speedMultiplier - - currentRotation - .slerp(rotationTo, turnSpeed) - .fixSensitivity(prevRotation) - .apply { - if (context.config.rotationMode != RotationMode.LOCK) return@apply - player.yaw = this.yawF - player.pitch = this.pitchF - } - } ?: player.rotation - } - private fun reset(rotation: Rotation) { prevRotation = rotation currentRotation = rotation - - currentContext = null - pauseTicks = 3 + currentRequest = null } private val smoothRotation get() = - lerp(mc.partialTicks, prevRotation, currentRotation) + lerp(Lambda.mc.partialTicks, prevRotation, currentRotation) @JvmStatic val lockRotation get() = - if (currentContext?.config?.rotationMode == RotationMode.LOCK) smoothRotation else null + if (currentRequest?.mode == RotationMode.LOCK) smoothRotation else null @JvmStatic val renderYaw get() = - if (currentContext?.config == null) null else smoothRotation.yaw.toFloat() + if (currentRequest == null) null else smoothRotation.yaw.toFloat() @JvmStatic val renderPitch get() = - if (currentContext?.config == null) null else smoothRotation.pitch.toFloat() + if (currentRequest == null) null else smoothRotation.pitch.toFloat() @JvmStatic val handYaw get() = - if (currentContext?.config?.rotationMode == RotationMode.LOCK) currentRotation.yaw.toFloat() else null + if (currentRequest?.mode == RotationMode.LOCK) currentRotation.yaw.toFloat() else null @JvmStatic val handPitch get() = - if (currentContext?.config?.rotationMode == RotationMode.LOCK) currentRotation.pitch.toFloat() else null + if (currentRequest?.mode == RotationMode.LOCK) currentRotation.pitch.toFloat() else null @JvmStatic val movementYaw: Float? get() { - if (currentContext?.config?.rotationMode == RotationMode.SILENT) return null + if (currentRequest?.mode == RotationMode.SILENT) return null return currentRotation.yaw.toFloat() } @JvmStatic val movementPitch: Float? get() { - if (currentContext?.config?.rotationMode == RotationMode.SILENT) return null + if (currentRequest?.mode == RotationMode.SILENT) return null return currentRotation.pitch.toFloat() } @JvmStatic fun getRotationForVector(deltaTime: Double): Vec2d? { - if (currentContext?.config?.rotationMode == RotationMode.SILENT) return null + if (currentRequest?.mode == RotationMode.SILENT) return null val rot = lerp(deltaTime, prevRotation, currentRotation) return Vec2d(rot.yaw, rot.pitch) @@ -226,11 +185,9 @@ object RotationManager : Loadable { object BaritoneProcessor { private var baritoneContext: RotationRequest? = null - - fun poolContext(): RotationRequest? { - val ctx = baritoneContext - baritoneContext = null - return ctx + private val baritoneRotation get() = Baritone.rotation.apply { + if (rotationMode == RotationMode.SYNC) return@apply + rotationMode = RotationMode.SYNC } private val movementYawList = arrayOf( @@ -242,16 +199,15 @@ object RotationManager : Loadable { @JvmStatic fun handleBaritoneRotation(yaw: Float, pitch: Float) { - baritoneContext = RotationRequest(Rotation(yaw, pitch), Baritone.rotation.apply { - if (rotationMode != RotationMode.SILENT) return@apply - rotationMode = RotationMode.SYNC - }) + lookAt( + Rotation(yaw, pitch) + ).requestBy(baritoneRotation) } @JvmStatic fun processPlayerMovement(input: Input, slowDown: Boolean, slowDownFactor: Float) = runSafe { // The yaw relative to which the movement was constructed - val baritoneYaw = baritoneContext?.rotation?.yaw + val baritoneYaw = baritoneContext?.target?.targetRotation?.value?.yaw val strafeEvent = RotationEvent.StrafeInput(baritoneYaw ?: player.yaw.toDouble(), input) val movementYaw = strafeEvent.post().strafeYaw @@ -269,7 +225,7 @@ object RotationManager : Loadable { // Actual yaw used by the physics engine var actualYaw = currentRotation.yaw - if (currentContext?.config?.rotationMode == RotationMode.SILENT) { + if (currentRequest?.mode == RotationMode.SILENT) { actualYaw = player.yaw.toDouble() } @@ -296,7 +252,7 @@ object RotationManager : Loadable { // when yaw difference is too big to compensate it by modifying keyboard input val minYawDist = movementYawList .map { currentRotation.yaw + it } // all possible movement directions (including diagonals) - .minOf { angleDifference(it, baritoneYaw) } + .minOf { Rotation.angleDifference(it, baritoneYaw) } if (minYawDist > 5.0) { input.movementSideways = 0f @@ -304,4 +260,4 @@ object RotationManager : Loadable { } } } -} +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt similarity index 95% rename from common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt index e59d10083..643fbeb13 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.interaction.rotation +package com.lambda.interaction.request.rotation /** * @property SILENT Spoofing server-side rotation. diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt new file mode 100644 index 000000000..fcda63935 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.rotation + +import com.lambda.interaction.request.Priority +import com.lambda.interaction.request.Request +import com.lambda.interaction.request.rotation.visibilty.RotationTarget +import com.lambda.threading.runSafe + +class RotationRequest( + val target: RotationTarget, + priority: Priority, + val mode: RotationMode, + var keepTicks: Int = 3, + var decayTicks: Int = 0, + val turnSpeed: () -> Double = { 180.0 }, + val speedMultiplier: Double = 1.0 +) : Request(priority) { + + constructor( + target: RotationTarget, + priority: Priority, + mode: RotationMode, + keepTicks: Int = 3, + decayTicks: Int = 0, + turnSpeed: Double = 180.0, + speedMultiplier: Double = 1.0 + ) : this(target, priority, mode, keepTicks, decayTicks, { turnSpeed }, speedMultiplier) + + constructor( + target: RotationTarget, + config: RotationConfig, + speedMultiplier: Double = 1.0 + ) : this(target, config.priority, config.rotationMode, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier) + + override val done: Boolean get() = + mode == RotationMode.NONE || runSafe { + target.verify(target) + } == true +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt new file mode 100644 index 000000000..39caf5c0c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PointSelection.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.rotation.visibilty + +import com.lambda.interaction.request.rotation.Rotation.Companion.dist +import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.util.math.distSq + +enum class PointSelection(val select: (MutableList) -> VisibilityChecker.CheckedHit?) { + ByRotation({ hits -> + hits.minByOrNull { + RotationManager.currentRotation dist it.targetRotation + } + }), + Optimum( optimum@ { hits -> + val optimum = hits.map { it.hit.pos }.reduceOrNull { acc, vec3d -> + acc.add(vec3d) + }?.multiply(1.0 / hits.size.toDouble()) ?: return@optimum null + + hits.minByOrNull { + it.hit.pos distSq optimum + } + }) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt new file mode 100644 index 000000000..2d8ecba72 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.rotation.visibilty + +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.ALL_SIDES +import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import com.lambda.util.world.raycast.RayCastUtils.entityResult +import net.minecraft.entity.LivingEntity +import net.minecraft.util.hit.HitResult +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.math.Direction + +/** + * An abstract class representing a rotation target, which can be either a block or an entity. + */ +abstract class RequestedHit private constructor( + val sides: Set, + val reach: Double +) { + + /** + * Verifies if the given [HitResult] satisfies the criteria of this + * requested hit. + * + * @param hit The [HitResult] to be verified. + * @return True if the hit satisfies the criteria, false otherwise. + */ + abstract fun verifyHit(hit: HitResult): Boolean + + /** + * Gets a list of bounding boxes associated with this requested hit. + * + * @return A list of [Box] objects representing the bounding boxes. + */ + abstract fun getBoundingBoxes(): List + + /** + * Validates the hit based on the current rotation and reach. + * + * @param rotation The rotation to be checked. + * + * @return True if the hit is valid, false otherwise. + */ + fun verifyRotation(rotation: Rotation = RotationManager.currentRotation) = + rotation.rayCast(reach)?.let { verifyHit(it) } ?: false + + /** + * Validates the hit based on the current rotation and reach and returns the hit result if passed. + * + * @return [HitResult] if passed, null otherwise. + */ + fun hitIfValid() = + RotationManager.currentRotation.rayCast(reach)?.let { + if (!verifyHit(it)) null else it + } + + /** + * Represents an entity hit request. + * + * @param entity The [LivingEntity] to be hit. + * @param reach The maximum distance for the hit. + */ + class Entity(val entity: LivingEntity, reach: Double) : RequestedHit(ALL_SIDES, reach) { + + override fun getBoundingBoxes() = + listOf(entity.boundingBox) + + override fun verifyHit(hit: HitResult) = + hit.entityResult?.entity == entity + } + + /** + * Represents a block hit request + * + * @param blockPos The position of the block. + * @param sides The set of directions for which the hit is requested. + * @param reach The maximum distance for the hit. + */ + class Block(val blockPos: BlockPos, sides: Set, reach: Double) : RequestedHit(sides, reach) { + + override fun getBoundingBoxes(): List = runSafe { + val state = blockPos.blockState(world) + val voxelShape = state.getOutlineShape(world, blockPos) + voxelShape.boundingBoxes.map { it.offset(blockPos) } + } ?: listOf(Box(blockPos)) + + override fun verifyHit(hit: HitResult) = + hit.blockResult?.let { + it.blockPos == blockPos && (sides.isEmpty() || it.side in sides) + } ?: false + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHitDsl.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHitDsl.kt new file mode 100644 index 000000000..2638b787b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHitDsl.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.rotation.visibilty + +import net.minecraft.entity.LivingEntity +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +@DslMarker +annotation class RequestedHitDsl + +@RequestedHitDsl +fun blockHit( + blockPos: BlockPos, + sides: Set, + reach: Double +) = RequestedHit.Block(blockPos, sides, reach) + +@RequestedHitDsl +fun blockHit( + blockPos: BlockPos, + side: Direction, + reach: Double +) = RequestedHit.Block(blockPos, setOf(side), reach) + +@RequestedHitDsl +fun entityHit( + entity: LivingEntity, + reach: Double +) = RequestedHit.Entity(entity, reach) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt new file mode 100644 index 000000000..d50dd02a4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.rotation.visibilty + +import com.lambda.context.SafeContext +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.threading.runSafe +import com.lambda.util.collections.cached + +/** + * Represents a target for rotation. + * + * @param hit The requested hit to look at. + * @param verify A lambda to check the active rotation. + * @param buildRotation A lambda that builds the rotation. + */ +class RotationTarget( + val hit: RequestedHit? = null, + val verify: RotationTarget.() -> Boolean = { hit?.verifyRotation() ?: true }, + private val buildRotation: SafeContext.() -> Rotation?, +) { + + val targetRotation = cached { + runSafe { buildRotation() } + } + + /** + * Requests a rotation based on the given configuration. + * + * @param config The rotation configuration. + * @return [RotationRequest] containing this [RotationTarget]. + */ + fun requestBy(config: RotationConfig) = + config.request(RotationRequest(this, config)) +} diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt new file mode 100644 index 000000000..0a8b28a70 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -0,0 +1,136 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.request.rotation.visibilty + +import com.lambda.config.groups.InteractionConfig +import com.lambda.context.SafeContext +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.dist +import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.ALL_SIDES +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.findRotation +import com.lambda.module.modules.client.TaskFlowModule +import com.lambda.util.world.raycast.InteractionMask +import net.minecraft.entity.LivingEntity +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.hit.EntityHitResult +import net.minecraft.util.hit.HitResult +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +@DslMarker +annotation class RotationDsl + +/** + * Creates a [RotationTarget] based on a specific angle. + * + * @param angle The target rotation. + * @param maxAngleDistance The maximum allowed distance between the current rotation and the target angle. Defaults to 10.0. + * @return A [RotationTarget] instance. + */ +@RotationDsl +fun lookAt(angle: Rotation, maxAngleDistance: Double = 10.0) = + RotationTarget(null, { + RotationManager.currentRotation dist angle < maxAngleDistance + }) { angle } + +/** + * Creates a [RotationTarget] based on a requested hit, but doesn't build the rotation. + * + * Use this, if you need to build the rotation by yourself. + * + * @param rotation The target rotation. + * @param hit [RequestedHit] to look at. + * @param rotation Custom rotation builder. + * @return A [RotationTarget] instance. + */ +@RotationDsl +fun lookAtHit(hit: RequestedHit, rotation: SafeContext.() -> Rotation?) = + RotationTarget(hit, buildRotation = rotation) + +/** + * Creates a [RotationTarget] based on a [HitResult]. + * + * @param hit [HitResult] to look at. + * @return A [RotationTarget] instance. + */ +@RotationDsl +fun lookAtHit( + hit: HitResult, + interactionConfig: InteractionConfig = TaskFlowModule.interact, +): RotationTarget? { + return when (hit) { + is BlockHitResult -> lookAtBlock(hit.blockPos, setOf(hit.side), interactionConfig) + is EntityHitResult -> lookAtEntity(hit.entity as? LivingEntity ?: return null, interactionConfig) + else -> null + } +} + +/** + * Creates a [RotationTarget] based on an entity. + * + * @param entity The target entity. + * @param interactionConfig The interaction configuration. Defaults to [TaskFlowModule.interact]. + * @return A [RotationTarget] instance. + */ +@RotationDsl +fun lookAtEntity( + entity: LivingEntity, + interactionConfig: InteractionConfig = TaskFlowModule.interact +): RotationTarget { + val requestedHit = entityHit(entity, interactionConfig.attackReach) + + return RotationTarget(requestedHit) { + findRotation( + requestedHit.getBoundingBoxes(), + interactionConfig.attackReach, + player.eyePos, + ALL_SIDES, + InteractionMask.ENTITY, + interactionConfig + ) { requestedHit.verifyHit(hit) }?.targetRotation + } +} + +/** + * Creates a [RotationTarget] based on a block. + * + * @param pos The position of the block. + * @param sides The set of sides to consider for the hit. Defaults to [ALL_SIDES]. + * @param interactionConfig The interaction configuration. Defaults to [TaskFlowModule.interact]. + * @return A [RotationTarget] instance. + */ +@RotationDsl +fun lookAtBlock( + pos: BlockPos, + sides: Set = ALL_SIDES, + interactionConfig: InteractionConfig = TaskFlowModule.interact, +): RotationTarget { + val requestedHit = blockHit(pos, sides, interactionConfig.placeReach) + + return RotationTarget(requestedHit) { + findRotation( + requestedHit.getBoundingBoxes(), + interactionConfig.placeReach, + player.eyePos, + sides, + InteractionMask.BLOCK, + interactionConfig + ) { requestedHit.verifyHit(hit) }?.targetRotation + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt similarity index 60% rename from common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt rename to common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt index 52dcd9f8d..4367644ea 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,26 +15,20 @@ * along with this program. If not, see . */ -package com.lambda.interaction.visibilty +package com.lambda.interaction.request.rotation.visibilty import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.RotationConfig +import com.lambda.config.groups.InteractionSettings import com.lambda.context.SafeContext -import com.lambda.interaction.RotationManager import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan -import com.lambda.interaction.rotation.Rotation.Companion.dist -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.module.modules.client.TaskFlowModule -import com.lambda.util.BlockUtils.blockState +import com.lambda.interaction.request.rotation.* +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.util.extension.component6 import com.lambda.util.math.distSq -import com.lambda.util.world.raycast.RayCastUtils.blockResult -import com.lambda.util.world.raycast.RayCastUtils.entityResult -import net.minecraft.entity.Entity +import com.lambda.util.world.raycast.InteractionMask +import net.minecraft.util.hit.EntityHitResult import net.minecraft.util.hit.HitResult -import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d @@ -46,106 +40,83 @@ import kotlin.math.pow */ object VisibilityChecker { - /** - * Attempts to rotate the player to look at a specified entity. - * The function calculates the best rotation to center the player's view on the bounding box of the given entity. - * - * @param rotationConfig Specifies the rotation configuration settings. - * @param interactionConfig Specifies interaction settings, such as range and resolution. - * @param entity The entity to be looked at. - * @return A [RotationRequest] if a valid rotation was found; otherwise, null. - */ - fun SafeContext.lookAtEntity( - entity: Entity, - rotationConfig: RotationConfig = TaskFlowModule.rotation, - interactionConfig: InteractionConfig = TaskFlowModule.interact, - ) = findRotation(listOf(entity.boundingBox), rotationConfig, interactionConfig) { - entityResult?.entity == entity - } - - /** - * Attempts to rotate the player to look at a specific block position. - * The function computes the best rotation to focus on a target block's position and side. - * - * @param blockPos The position of the block to look at. - * @param rotationConfig Specifies rotation configuration settings. - * @param interactionConfig Specifies interaction settings, such as range and resolution. - * @param sides Specifies the set of block sides to consider for targeting. - * @return A [RotationRequest] if a valid rotation was found; otherwise, null. - */ - fun SafeContext.lookAtBlock( - blockPos: BlockPos, - rotationConfig: RotationConfig = TaskFlowModule.rotation, - interactionConfig: InteractionConfig = TaskFlowModule.interact, - sides: Set = Direction.entries.toSet(), - ): RotationRequest? { - val state = blockPos.blockState(world) - val voxelShape = state.getOutlineShape(world, blockPos) - val boundingBoxes = voxelShape.boundingBoxes.map { it.offset(blockPos) } - return findRotation(boundingBoxes, rotationConfig, interactionConfig, sides) { - blockResult?.blockPos == blockPos && (blockResult?.side in sides || sides.isEmpty()) - } - } - /** * Finds a rotation that intersects with one of the specified bounding boxes, allowing the player to look at entities or blocks. + * To increase the stability, it will pause the rotation if eye position is within any of the bounding boxes * * @param boxes List of bounding boxes for potential targets. - * @param rotationConfig Specifies rotation configuration settings. - * @param interact Specifies interaction settings, such as range and resolution. - * @param sides Set of block sides to consider for targeting. * @param reach The maximum reach distance for the interaction. * @param eye The player's eye position. - * @param verify A lambda to verify if a [HitResult] meets the desired criteria. - * @return A [RotationRequest] if a valid rotation was found; otherwise, null. + * @param sides Set of block sides to consider for targeting. + * @param interaction Specifies interaction settings, such as side visibility and resolution. + * @param verify A lambda to verify if a [CheckedHit] meets the desired criteria. + * + * @return A [CheckedHit] if a valid rotation was found; otherwise, null. */ fun SafeContext.findRotation( boxes: List, - rotationConfig: RotationConfig = TaskFlowModule.rotation, - interact: InteractionConfig = TaskFlowModule.interact, - sides: Set = Direction.entries.toSet(), - reach: Double = interact.reach, - eye: Vec3d = player.getCameraPosVec(1f), - verify: HitResult.() -> Boolean, - ): RotationRequest? { + reach: Double, + eye: Vec3d, + sides: Set, + targetType: InteractionMask, + interaction: InteractionConfig, + verify: CheckedHit.() -> Boolean + ): CheckedHit? { val currentRotation = RotationManager.currentRotation - val currentCast = currentRotation.rayCast(reach, eye) if (boxes.any { it.contains(eye) }) { - return RotationRequest(currentRotation, rotationConfig, currentCast, verify) + currentRotation.rayCast(reach, eye)?.let { hit -> + return CheckedHit(hit, currentRotation, reach) + } } - val validHits = mutableMapOf() - val reachSq = reach.pow(2) + return interaction.pointSelection.select( + collectHitsFor(boxes, reach, eye, sides, SurfaceScan.DEFAULT, targetType, interaction, verify) + ) + } + + /** + * Finds a collection of [CheckedHit] that intersect with one of the specified bounding boxes, allowing the player to look at entities or blocks. + * + * @param boxes List of bounding boxes for potential targets. + * @param reach The maximum reach distance for the interaction. + * @param eye The player's eye position. + * @param sides Set of block sides to consider for targeting. + * @param scan Configuration specifying the axis and mode of the scan (default is `SurfaceScan.DEFAULT`). + * @param interaction Specifies interaction settings, such as side visibility and resolution. + * @param verify A lambda to verify if a [CheckedHit] meets the desired criteria. + * + * @return A collection of [CheckedHit] with valid angles found + */ + fun SafeContext.collectHitsFor( + boxes: List, + reach: Double, + eye: Vec3d = player.eyePos, + sides: Set = ALL_SIDES, + scan: SurfaceScan = SurfaceScan.DEFAULT, + targetType: InteractionMask, + interaction: InteractionConfig, + verify: CheckedHit.() -> Boolean, + ) = mutableListOf().apply { + val reachSq = interaction.scanReach.pow(2) boxes.forEach { box -> - val visible = visibleSides(box, eye, interact) + val visible = visibleSides(box, eye, interaction.checkSideVisibility) - scanSurfaces(box, visible.intersect(sides), interact.resolution) { _, vec -> + scanSurfaces(box, visible.intersect(sides), interaction.resolution, scan) { _, vec -> if (eye distSq vec > reachSq) return@scanSurfaces val newRotation = eye.rotationTo(vec) - val cast = newRotation.rayCast(reach, eye) ?: return@scanSurfaces - if (!cast.verify()) return@scanSurfaces + val mask = if (interaction.strictRayCast) InteractionMask.BOTH else targetType + val hit = newRotation.rayCast(reach, eye, mask = mask) ?: return@scanSurfaces - validHits[vec] = cast - } - } + val checked = CheckedHit(hit, newRotation, reach) + if (!checked.verify()) return@scanSurfaces - // Way stable - validHits.minByOrNull { eye.rotationTo(it.key) dist currentRotation }?.let { closest -> - return RotationRequest(eye.rotationTo(closest.key), rotationConfig, closest.value, verify) - } - - /*validHits.keys.optimum?.let { optimum -> - validHits.minByOrNull { optimum distSq it.key }?.let { closest -> - val optimumRotation = eye.rotationTo(closest.key) - return RotationRequest(optimumRotation, rotationConfig, closest.value, verify) + add(checked) } - }*/ - - return null + } } /** @@ -158,7 +129,7 @@ object VisibilityChecker { * @param scan Configuration specifying the axis and mode of the scan (default is `SurfaceScan.DEFAULT`). * @param check A callback function that performs an action for each surface point, receiving the direction of the surface and the current 3D vector. */ - inline fun scanSurfaces( + fun scanSurfaces( box: Box, excludedSides: Set = emptySet(), resolution: Int = 5, @@ -214,24 +185,16 @@ object VisibilityChecker { } } - /** - * Determines the approximate central point (optimum) of a set of 3D vectors. - */ - val Set.optimum: Vec3d? - get() = reduceOrNull { acc, vec3d -> - acc.add(vec3d) - }?.multiply(1.0 / size.toDouble()) - /** * Determines the sides of a box that are visible from a given position, based on interaction settings. * * @param box The box whose visible sides are to be determined. * @param eye The position (e.g., the player's eyes) to determine visibility from. - * @param interactionSettings The settings that define how visibility checks are handled. + * @param visibilityCheck Whether to check the visibility of the side. * @return A set of directions corresponding to the visible sides of the box. */ - fun visibleSides(box: Box, eye: Vec3d, interactionSettings: InteractionConfig) = - if (interactionSettings.visibilityCheck) { + private fun visibleSides(box: Box, eye: Vec3d, visibilityCheck: Boolean) = + if (visibilityCheck) { box.getVisibleSurfaces(eye) } else Direction.entries.toSet() @@ -241,7 +204,7 @@ object VisibilityChecker { * @param side The side of the box to calculate bounds for. * @return An array of doubles representing the side's bounds. */ - fun Box.bounds(side: Direction) = + private fun Box.bounds(side: Direction) = when (side) { Direction.DOWN -> doubleArrayOf(minX, minY, minZ, maxX, minY, maxZ) Direction.UP -> doubleArrayOf(minX, maxY, minZ, maxX, maxY, maxZ) @@ -277,4 +240,8 @@ object VisibilityChecker { diff > limit -> add(positiveSide) } } + + val ALL_SIDES = Direction.entries.toSet() + + class CheckedHit(val hit: HitResult, val targetRotation: Rotation, val reach: Double) } diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt deleted file mode 100644 index 28d1ab7d2..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationRequest.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.interaction.rotation - -import com.lambda.config.groups.RotationConfig -import com.lambda.interaction.RotationManager -import com.lambda.threading.runSafe -import com.lambda.util.world.raycast.RayCastUtils.orMiss -import net.minecraft.util.hit.HitResult - -data class RotationRequest( - val rotation: Rotation, - val config: RotationConfig, - val checkedResult: HitResult? = null, - val verify: HitResult.() -> Boolean = { true }, -) { - val cast: HitResult? get() = runSafe { RotationManager.currentRotation.rayCast(10.0, player.eyePos) } - val isValid: Boolean get() = runSafe { - // ToDo: Use proper reach - val result = cast - verify(result.orMiss) - } ?: false -} diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt index a6e7ac9ae..fc79bf034 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt @@ -26,5 +26,5 @@ object Baritone : Module( description = "Baritone configuration", defaultTags = setOf(ModuleTag.CLIENT) ) { - val rotation = RotationSettings(this) + val rotation = RotationSettings(this, Int.MIN_VALUE) } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt index 616f1d628..ac1b56d33 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt @@ -21,11 +21,13 @@ import com.lambda.config.groups.BuildSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings import com.lambda.config.groups.RotationSettings +import com.lambda.core.PingManager import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.result.Drawable import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.world.raycast.InteractionMask object TaskFlowModule : Module( name = "TaskFlow", @@ -39,7 +41,21 @@ object TaskFlowModule : Module( private val page by setting("Page", Page.BUILD) val build = BuildSettings(this) { page == Page.BUILD } val rotation = RotationSettings(this) { page == Page.ROTATION } - val interact = InteractionSettings(this) { page == Page.INTERACTION } + val interact = InteractionSettings(this, InteractionMask.BOTH) { page == Page.INTERACTION } + + // ToDo: remove + // might be useless since grim has ping compensation things and shit + // its better to make sure the interactions are 100% matching the minecraft's ones + private val pingTimeout by setting("Ping Timeout", false, "Timeout on high ping") { page == Page.INTERACTION } + private val inScopeThreshold by setting("Constant Timeout", 1, 0..20, 1, "How many ticks to wait after target box is in rotation scope"," ticks") { + page == Page.INTERACTION && !pingTimeout + } + + val scopeThreshold: Int + get() = + if (pingTimeout) PingManager.lastPing.toInt() / 50 + else inScopeThreshold + val inventory = InventorySettings(this) { page == Page.INVENTORY } val showAllEntries by setting("Show All Entries", false, "Show all entries in the task tree") { page == Page.DEBUG } @@ -54,4 +70,6 @@ object TaskFlowModule : Module( } } } + + } diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt index a46b24721..d06862dc0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt @@ -20,8 +20,8 @@ package com.lambda.module.modules.combat import com.lambda.context.SafeContext import com.lambda.event.events.PlayerEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.rotation.Rotation -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.extension.rotation diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 902e5f848..4ad39bcfb 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -30,9 +30,10 @@ import com.lambda.graphics.gl.Matrices.withVertexTransform import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.font.LambdaEmoji import com.lambda.graphics.renderer.gui.font.LambdaFont -import com.lambda.interaction.RotationManager.rotate -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.rotation.RotationRequest +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe @@ -86,7 +87,10 @@ object CrystalAura : Module( /* Prediction */ private val prediction by setting("Prediction", PredictionMode.None) { page == Page.Prediction } + private val packetPredictions by setting("Packet Predictions", 1, 0..20, 1) { page == Page.Prediction && prediction.onPacket } + private val placePostPause by setting("Place Post Pause", true) { page == Page.Prediction && prediction.onPacket } + private val placePredictions by setting("Place Predictions", 4, 1..20, 1) { page == Page.Prediction && prediction.onPlace } private val packetLifetime by setting("Packet Lifetime", 500L, 50L..1000L) { page == Page.Prediction && prediction.onPlace } @@ -195,11 +199,11 @@ object CrystalAura : Module( if (!prediction.onPacket) return@listen repeat(packetPredictions) { - placeInternal(pos, Hand.MAIN_HAND) + placeInternal(opportunity, Hand.MAIN_HAND) explodeInternal(++lastEntityId) } - placeTimer.reset() + if (placePostPause) placeTimer.reset() } listen { event -> @@ -214,18 +218,6 @@ object CrystalAura : Module( currentTarget = null resetBlueprint() } - - rotate { - request { - activeOpportunity?.let { - RotationRequest( - player.eyePos.rotationTo( - it.blockPos.crystalPosition - ), rotation - ) - } - } - } } private fun SafeContext.tick() { @@ -266,10 +258,10 @@ object CrystalAura : Module( best.place() } - private fun SafeContext.placeInternal(blockPos: BlockPos, hand: Hand) { + private fun SafeContext.placeInternal(opportunity: Opportunity, hand: Hand) { connection.sendPacket( PlayerInteractBlockC2SPacket( - hand, BlockHitResult(blockPos.crystalPosition, Direction.UP, blockPos, false), 0 + hand, BlockHitResult(opportunity.crystalPosition, opportunity.side, opportunity.blockPos, false), 0 ) ) @@ -290,117 +282,117 @@ object CrystalAura : Module( updateTimer.runIfPassed(updateDelay.toDuration(DurationUnit.MILLISECONDS)) { resetBlueprint() - // Build damage info - fun info( - pos: BlockPos, target: LivingEntity, - blocked: Boolean, - crystal: EndCrystalEntity? = null - ): Opportunity? { - val crystalPos = pos.crystalPosition - - // Calculate the damage to the target from the explosion of the crystal - val targetDamage = crystalDamage(crystalPos, target) - if (targetDamage < minTargetDamage) return null - - // Calculate the self-damage for the player - val selfDamage = crystalDamage(crystalPos, player) - if (selfDamage > maxSelfDamage) return null - - if (priorityMode == Priority.Advantage && priorityMode.factor(targetDamage, selfDamage) < minDamageAdvantage) return null - - // Return the calculated damage info if conditions are met - return Opportunity( - pos.toImmutable(), - targetDamage, - selfDamage, - blocked, - crystal - ) - } - - // Extra checks for placement, because you may explode but not place in special cases(crystal in the air) - @Suppress("ConvertArgumentToSet") - fun placeInfo( - pos: BlockPos, - target: LivingEntity - ): Opportunity? { - // Check if crystals could be placed on the base block - val state = pos.blockState(world) - val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) - if (!isOfBlock) return null - - // Check if the block above is air and other conditions for valid crystal placement - val above = pos.up() - if (!world.isAir(above)) return null - if (oldPlace && !world.isAir(above.up())) return null - - // Exclude blocks blocked by entities - val crystalBox = pos.crystalBox - - val entitiesNearby = fastEntitySearch(3.5, pos) - val crystals = entitiesNearby.filterIsInstance() as MutableList - val otherEntities = entitiesNearby - crystals + player - - if (otherEntities.any { - it.boundingBox.intersects(crystalBox) - }) return null - - // Placement collision checks - val baseCrystal = crystals.firstOrNull { - it.baseBlockPos == pos - } + // Build damage info + fun info( + pos: BlockPos, target: LivingEntity, + blocked: Boolean, + crystal: EndCrystalEntity? = null + ): Opportunity? { + val crystalPos = pos.crystalPosition + + // Calculate the damage to the target from the explosion of the crystal + val targetDamage = crystalDamage(crystalPos, target) + if (targetDamage < minTargetDamage) return null + + // Calculate the self-damage for the player + val selfDamage = crystalDamage(crystalPos, player) + if (selfDamage > maxSelfDamage) return null + + if (priorityMode == Priority.Advantage && priorityMode.factor(targetDamage, selfDamage) < minDamageAdvantage) return null + + // Return the calculated damage info if conditions are met + return Opportunity( + pos.toImmutable(), + targetDamage, + selfDamage, + blocked, + crystal + ) + } - val crystalPlaceBox = pos.crystalPlaceHitBox - val blocked = baseCrystal == null && crystals.any { - it.boundingBox.intersects(crystalPlaceBox) - } + // Extra checks for placement, because you may explode but not place in special cases(crystal in the air) + @Suppress("ConvertArgumentToSet") + fun placeInfo( + pos: BlockPos, + target: LivingEntity + ): Opportunity? { + // Check if crystals could be placed on the base block + val state = pos.blockState(world) + val isOfBlock = state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK) + if (!isOfBlock) return null + + // Check if the block above is air and other conditions for valid crystal placement + val above = pos.up() + if (!world.isAir(above)) return null + if (oldPlace && !world.isAir(above.up())) return null + + // Exclude blocks blocked by entities + val crystalBox = pos.crystalBox + + val entitiesNearby = fastEntitySearch(3.5, pos) + val crystals = entitiesNearby.filterIsInstance() as MutableList + val otherEntities = entitiesNearby - crystals + player + + if (otherEntities.any { + it.boundingBox.intersects(crystalBox) + }) return null + + // Placement collision checks + val baseCrystal = crystals.firstOrNull { + it.baseBlockPos == pos + } - return info( - pos, - target, - blocked, - baseCrystal - ) + val crystalPlaceBox = pos.crystalPlaceHitBox + val blocked = baseCrystal == null && crystals.any { + it.boundingBox.intersects(crystalPlaceBox) } - val range = max(placeRange, explodeRange) + 1 - val rangeInt = range.ceilToInt() + return info( + pos, + target, + blocked, + baseCrystal + ) + } - // Iterate through existing crystals - val crystalBase = BlockPos.Mutable() - fastEntitySearch(range).forEach { crystal -> - crystalBase.set(crystal.x, crystal.y - 0.5, crystal.z) - damage += info(crystalBase, target, false, crystal) ?: return@forEach - } + val range = max(placeRange, explodeRange) + 1 + val rangeInt = range.ceilToInt() - // Iterate through possible place positions and calculate damage information for each - BlockPos.iterateOutwards(player.blockPos.up(), rangeInt, rangeInt, rangeInt).forEach { pos -> - if (pos distSq player.pos > range * range) return@forEach - if (damage.any { info -> info.blockPos == pos }) return@forEach + // Iterate through existing crystals + val crystalBase = BlockPos.Mutable() + fastEntitySearch(range).forEach { crystal -> + crystalBase.set(crystal.x, crystal.y - 0.5, crystal.z) + damage += info(crystalBase, target, false, crystal) ?: return@forEach + } - damage += placeInfo(pos, target) ?: return@forEach - } + // Iterate through possible place positions and calculate damage information for each + BlockPos.iterateOutwards(player.blockPos.up(), rangeInt, rangeInt, rangeInt).forEach { pos -> + if (pos distSq player.pos > range * range) return@forEach + if (damage.any { info -> info.blockPos == pos }) return@forEach - // Map opportunities - damage.forEach { - blueprint[it.blockPos] = it - } + damage += placeInfo(pos, target) ?: return@forEach + } - // Associate by actions - blueprint.values.forEach { opportunity -> - actionMap.getOrPut(opportunity.actionType, ::mutableListOf) += opportunity + // Map opportunities + damage.forEach { + blueprint[it.blockPos] = it + } - if (opportunity.actionType.priority > actionType.priority) { - actionType = opportunity.actionType - } - } + // Associate by actions + blueprint.values.forEach { opportunity -> + actionMap.getOrPut(opportunity.actionType, ::mutableListOf) += opportunity - // Select best action - activeOpportunity = actionMap[actionType]?.maxByOrNull { - it.priority + if (opportunity.actionType.priority > actionType.priority) { + actionType = opportunity.actionType } } + // Select best action + activeOpportunity = actionMap[actionType]?.maxByOrNull { + it.priority + } + } + private fun resetBlueprint() { blueprint.clear() damage.clear() @@ -429,38 +421,67 @@ object CrystalAura : Module( val priority = priorityMode.factor(target, self) val hasCrystal get() = crystal != null + val crystalPosition by lazy { + blockPos.crystalPosition + } + + val side by lazy { + runSafe { + val visibleSides = Box(blockPos).getVisibleSurfaces(player.eyePos) + if (visibleSides.contains(Direction.UP)) Direction.UP else visibleSides.minByOrNull { + blockPos.getHitVec(it) distSq player.eyePos + } + } ?: Direction.UP + } + + val placeRotation by lazy { + runSafe { + var vec = blockPos.getHitVec(side) + + // look at the top part of the side + if (side.axis != Direction.Axis.Y) vec += Vec3d(0.0, 0.45, 0.0) + + player.eyePos.rotationTo(vec) + } ?: RotationManager.currentRotation + } + /** * Places the crystal on [blockPos] * @return Whether the delay passed, null if the interaction failed */ - fun place() = placeTimer.runSafeIfPassed(placeDelay.toDuration(DurationUnit.MILLISECONDS)) { - placeInternal(blockPos, Hand.MAIN_HAND) + fun place() { + if (rotation.rotate && !lookAt(placeRotation).requestBy(rotation).done) return - if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime.toDuration(DurationUnit.MILLISECONDS), false) { - val last = lastEntityId + placeTimer.runSafeIfPassed(placeDelay.toDuration(DurationUnit.MILLISECONDS)) { + placeInternal(this@Opportunity, Hand.MAIN_HAND) - repeat(placePredictions) { - explodeInternal(++lastEntityId) - } + if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime.toDuration(DurationUnit.MILLISECONDS), false) { + val last = lastEntityId - lastEntityId = last + 1 - crystal = null - } + repeat(placePredictions) { + explodeInternal(++lastEntityId) + } - placeTimer.reset() + lastEntityId = last + 1 + crystal = null + } + } } /** * Explodes a crystal that is on [blockPos] * @return Whether the delay passed, null if the interaction failed or no crystal found */ - fun explode() = + fun explode() { + if (rotation.rotate && !lookAt(placeRotation).requestBy(rotation).done) return + explodeTimer.runSafeIfPassed(explodeDelay.toDuration(DurationUnit.MILLISECONDS)) { crystal?.let { crystal -> explodeInternal(crystal.id) explodeTimer.reset() } } + } fun buildDebug() { withVertexTransform(buildWorldProjection(blockPos.crystalPosition, 0.4, Matrices.ProjRotationMode.TO_CAMERA)) { diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index 1054c1b2c..5e55e14c6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -25,23 +25,12 @@ import com.lambda.event.events.PacketEvent import com.lambda.event.events.PlayerPacketEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.RotationManager -import com.lambda.interaction.RotationManager.rotate -import com.lambda.interaction.rotation.Rotation -import com.lambda.interaction.rotation.Rotation.Companion.dist -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.visibilty.VisibilityChecker.scanSurfaces -import com.lambda.interaction.visibilty.VisibilityChecker.visibleSides +import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotation.visibilty.lookAtEntity import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.MathUtils.random -import com.lambda.util.math.distSq -import com.lambda.util.math.plus -import com.lambda.util.math.times -import com.lambda.util.math.lerp -import com.lambda.util.player.MovementUtils.moveDiff -import com.lambda.util.player.prediction.buildPlayerPrediction +import com.lambda.util.world.raycast.InteractionMask import com.lambda.util.world.raycast.RayCastUtils.entityResult import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity @@ -52,8 +41,8 @@ import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket import net.minecraft.util.Hand import net.minecraft.util.math.Vec3d -import kotlin.math.pow +// ToDo: Rewrite me plz object KillAura : Module( name = "KillAura", description = "Attacks entities", @@ -62,7 +51,7 @@ object KillAura : Module( private val page by setting("Page", Page.Interact) // Interact - private val interactionSettings = InteractionSettings(this, useDefaultReach = false) { page == Page.Interact } + private val interactionSettings = InteractionSettings(this, InteractionMask.ENTITY) { page == Page.Interact } private val attackMode by setting("Attack Mode", AttackMode.Cooldown) { page == Page.Interact } private val delaySync by setting("Client-side Delay", true) { page == Page.Interact && attackMode == AttackMode.Cooldown } private val cooldownSync by setting("Client-side Cooldown", true) { page == Page.Interact && attackMode == AttackMode.Cooldown } @@ -90,6 +79,7 @@ object KillAura : Module( get() = targeting.target() private var shakeRandom = Vec3d.ZERO + private var speedMultiplier = 1.0 private var attackTicks = 0 private var lastAttackTime = 0L @@ -112,16 +102,6 @@ object KillAura : Module( } init { - rotate { - request { - if (!rotate) return@request null - - target?.let { target -> - buildRotation(target) - } - } - } - listen(Int.MIN_VALUE) { event -> prevY = lastY lastY = event.position.y @@ -131,8 +111,11 @@ object KillAura : Module( listen { if (!timerSync) attackTicks++ + target?.let { entity -> - runAttack(entity) + if (lookAtEntity(entity).requestBy(rotation).done) { + runAttack(entity) + } } } @@ -149,7 +132,7 @@ object KillAura : Module( onDisable(::reset) } - private fun SafeContext.buildRotation(target: LivingEntity): RotationRequest? { + /*private fun SafeContext.buildRotation(target: LivingEntity) { val currentRotation = RotationManager.currentRotation val prediction = buildPlayerPrediction() @@ -174,15 +157,9 @@ object KillAura : Module( val box = target.boundingBox val reach = targeting.targetingRange + 2.0 - val reachSq = reach.pow(2) - - // Do not rotate if the eyes are inside the target's AABB - if (box.contains(eye)) { - return RotationRequest(currentRotation, rotation) - } // Rotation stabilizer - rotation.speedMultiplier = if (stabilize && !rotation.instant) { + speedMultiplier = if (stabilize && !rotation.instant) { val slowDown = currentRotation.castBox(box, reach, eye) != null with(rotation) { @@ -229,28 +206,27 @@ object KillAura : Module( if (vecRotation.rayCast(reach, eye)?.entityResult?.entity == target) return@run // Get visible point set - val validHits = mutableMapOf() - - val sides = visibleSides(box, eye, interactionSettings) - - scanSurfaces(box, sides, resolution = interactionSettings.resolution) { _, vec -> - if (eye distSq vec > reachSq) return@scanSurfaces - - val newRotation = eye.rotationTo(vec) - - val cast = newRotation.rayCast(reach, eye) ?: return@scanSurfaces - if (cast.entityResult?.entity != target) return@scanSurfaces - - validHits[vec] = newRotation + val validHits = collectHitsFor( + listOf(target.boundingBox), + reach + ) { + hit.entityResult?.entity == target } // Switch to the closest visible point - vec = validHits.minByOrNull { vecRotation dist it.value }?.key ?: return null + //vec = validHits.minByOrNull { vecRotation dist it.value }?.key ?: return null } val predictOffset = target.moveDiff * targetPredict - return RotationRequest(eye.rotationTo(vec + predictOffset), rotation) - } + + return RotationRequest( + eye.rotationTo(), + rotation, + speedMultiplier + ) { + rayCast(reach, eye)?.entityResult == target + } + }*/ private fun SafeContext.runAttack(target: LivingEntity) { // Critical hit check @@ -283,13 +259,13 @@ object KillAura : Module( if (!rotate) return@run val angle = RotationManager.currentRotation - if (interactionSettings.useRayCast) { - val cast = angle.rayCast(interactionSettings.reach) + if (interactionSettings.strictRayCast) { + val cast = angle.rayCast(interactionSettings.attackReach) if (cast?.entityResult?.entity != target) return } // Perform a raycast without checking the environment - angle.castBox(target.boundingBox, interactionSettings.reach) ?: return + angle.castBox(target.boundingBox, interactionSettings.attackReach) ?: return } // Attack @@ -315,7 +291,7 @@ object KillAura : Module( private fun reset(ctx: SafeContext) = ctx.apply { attackTicks = player.lastAttackedTicks - rotation.speedMultiplier = 1.0 + speedMultiplier = 1.0 shakeRandom = Vec3d.ZERO lastY = 0.0 diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index f3b215f85..6a90d0e20 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -17,15 +17,13 @@ package com.lambda.module.modules.movement -import com.lambda.config.groups.RotationConfig import com.lambda.context.SafeContext import com.lambda.event.events.ClientEvent import com.lambda.event.events.MovementEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.RotationManager.rotate -import com.lambda.interaction.rotation.Rotation -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.rotation.RotationMode +import com.lambda.interaction.request.rotation.* +import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.NamedEnum @@ -71,9 +69,7 @@ object Speed : Module( private val ncpTimerBoost by setting("Timer Boost", 1.08, 1.0..1.1, 0.01) { mode == Mode.NCP_STRAFE } // Grim - private val rotationConfig = object : RotationConfig.Instant { - override val rotationMode = RotationMode.SYNC - } + private val rotationConfig = RotationConfig.Instant(RotationMode.SYNC, Int.MIN_VALUE + 1) private var prevTickJumping = false @@ -134,34 +130,33 @@ object Speed : Module( } } - rotate(100, alwaysListen = false) { - request { lastContext -> - if (mode != Mode.GRIM_STRAFE) return@request null - if (!shouldWork()) return@request null + onRotate { + if (mode != Mode.GRIM_STRAFE) return@onRotate + if (!shouldWork()) return@onRotate - var yaw = player.yaw - val input = newMovementInput() + var yaw = player.yaw + val input = newMovementInput() - if (!input.isInputting) return@request null + if (!input.isInputting) return@onRotate - run { - if (!diagonal) return@run - if (player.isOnGround && input.jumping) return@run + run { + if (!diagonal) return@run + if (player.isOnGround && input.jumping) return@run - val forward = input.roundedForward.toFloat() - var strafe = input.roundedStrafing.toFloat() + val forward = input.roundedForward.toFloat() + var strafe = input.roundedStrafing.toFloat() - if (strafe == 0f) strafe = -1f - if (forward == 0f) strafe *= -1 + if (strafe == 0f) strafe = -1f + if (forward == 0f) strafe *= -1 - yaw -= 45 * strafe - } + yaw -= 45 * strafe + } - val moveYaw = calcMoveYaw(yaw, input.roundedForward, input.roundedStrafing) - val rotation = Rotation(moveYaw, lastContext?.rotation?.pitch ?: player.pitch.toDouble()) + val moveYaw = calcMoveYaw(yaw, input.roundedForward, input.roundedStrafing) - RotationRequest(rotation, rotationConfig) - } + lookAt( + Rotation(moveYaw, 0.0) + ).requestBy(rotationConfig) } onEnable { diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt index b80138325..4c118ce55 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt @@ -20,7 +20,7 @@ package com.lambda.module.modules.movement import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.module.Module import com.lambda.module.modules.combat.KillAura import com.lambda.module.tag.ModuleTag diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt index 2698d4fda..6d464dc82 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt @@ -18,13 +18,15 @@ package com.lambda.module.modules.player import com.lambda.Lambda.mc -import com.lambda.config.groups.RotationConfig +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.event.events.* import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.rotation.Rotation -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.rotation.RotationMode +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationMode +import com.lambda.interaction.request.rotation.visibilty.lookAt +import com.lambda.interaction.request.rotation.visibilty.lookAtHit import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.extension.partialTicks @@ -56,9 +58,7 @@ object Freecam : Module( private val reach by setting("Reach", 10.0, 1.0..100.0, 1.0, "Freecam reach distance") private val rotateToTarget by setting("Rotate to target", true) - private val rotationConfig = object : RotationConfig.Instant { - override val rotationMode = RotationMode.LOCK - } + private val rotationConfig = RotationConfig.Instant(RotationMode.LOCK) private var lastPerspective = Perspective.FIRST_PERSON private var prevPosition: Vec3d = Vec3d.ZERO @@ -94,12 +94,12 @@ object Freecam : Module( mc.options.perspective = lastPerspective } - listen(Int.MAX_VALUE) { event -> - if (!rotateToTarget) return@listen - val target = mc.crosshairTarget?.orNull ?: return@listen + onRotate { + if (!rotateToTarget) return@onRotate - val rotation = player.eyePos.rotationTo(target.pos) - event.request = RotationRequest(rotation, rotationConfig) + mc.crosshairTarget?.let { + lookAtHit(it)?.requestBy(rotationConfig) + } } listen { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 8e32368d9..4d2a7afc3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -25,9 +25,11 @@ import com.lambda.graphics.renderer.esp.DynamicAABB import com.lambda.graphics.renderer.esp.builders.buildFilled import com.lambda.graphics.renderer.esp.builders.buildOutline import com.lambda.graphics.renderer.esp.global.DynamicESP -import com.lambda.interaction.RotationManager -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.visibilty.VisibilityChecker.findRotation +import com.lambda.interaction.request.rotation.RotationManager +import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.collectHitsFor +import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.module.Module import com.lambda.module.modules.client.TaskFlowModule import com.lambda.module.tag.ModuleTag @@ -531,24 +533,18 @@ object PacketMine : Module( } } - listen { - if (!rotate.isEnabled()) return@listen + onRotate { + if (!rotate.isEnabled()) return@onRotate rotationPosition?.let { pos -> lastNonEmptyState?.let { state -> - val boxList = state.getOutlineShape(world, pos).boundingBoxes.map { it.offset(pos) } - val rotationRequest = - findRotation(boxList, TaskFlowModule.rotation, TaskFlowModule.interact, emptySet()) { true } - rotationRequest?.let { request -> - it.request = request - expectedRotation = request - } + expectedRotation = lookAtBlock(pos).requestBy(TaskFlowModule.rotation) } } ?: run { expectedRotation = null } - if (!rotated || !waitingToReleaseRotation) return@listen + if (!rotated || !waitingToReleaseRotation) return@onRotate releaseRotateDelayCounter-- @@ -573,7 +569,7 @@ object PacketMine : Module( if (verifyRotation( boxList, RotationManager.currentRotation.vector, - RotationManager.currentContext?.checkedResult + RotationManager.currentRequest?.target?.hit?.hitIfValid() ) ) { onRotationComplete?.run() @@ -899,7 +895,7 @@ object PacketMine : Module( if (!verifyRotation( lastNonEmptyState?.getOutlineShape(world, pos)?.boundingBoxes?.map { it.offset(pos) }, RotationManager.currentRotation.vector, - RotationManager.currentContext?.checkedResult + RotationManager.currentRequest?.target?.hit?.hitIfValid() ) ) { pausedForRotation = true diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index 023675037..221579212 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -19,7 +19,7 @@ package com.lambda.module.modules.player import com.google.gson.* import com.lambda.brigadier.CommandResult -import com.lambda.config.groups.RotationConfig +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.core.TimerManager import com.lambda.event.EventFlow.lambdaScope @@ -27,9 +27,11 @@ import com.lambda.event.events.KeyboardEvent import com.lambda.event.events.MovementEvent import com.lambda.event.events.RotationEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.rotation.Rotation -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.rotation.RotationMode +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.RotationMode +import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.player.Replay.InputAction.Companion.toAction @@ -79,7 +81,7 @@ object Replay : Module( private val deviationThreshold by setting("Deviation threshold", 0.1, 0.1..5.0, 0.1, description = "The threshold for the deviation to cancel the replay.") { cancelOnDeviation } private val lockCamera by setting("Lock Camera", true) - private val rotationConfig = object : RotationConfig.Instant { + private val rotationConfig = object : RotationConfig.Instant(RotationMode.SYNC) { override val rotationMode = if (lockCamera) RotationMode.LOCK else RotationMode.SYNC } @@ -157,7 +159,7 @@ object Replay : Module( } } - listen { event -> + onRotate { when (state) { State.RECORDING -> { buffer?.rotation?.add(player.rotation) @@ -165,7 +167,7 @@ object Replay : Module( State.PLAYING -> { buffer?.rotation?.removeFirstOrNull()?.let { rot -> - event.request = RotationRequest(rot, rotationConfig) + lookAt(rot).requestBy(rotationConfig) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index a694f2d80..2705e5bc1 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -27,19 +27,21 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh import com.lambda.graphics.renderer.esp.builders.ofBox -import com.lambda.interaction.RotationManager.currentRotation -import com.lambda.interaction.RotationManager.rotate +import com.lambda.interaction.request.rotation.RotationManager.currentRotation +import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.blockplace.PlaceFinder.Companion.buildPlaceInfo import com.lambda.interaction.blockplace.PlaceInfo import com.lambda.interaction.blockplace.PlaceInteraction.placeBlock -import com.lambda.interaction.rotation.Rotation -import com.lambda.interaction.rotation.Rotation.Companion.angleDifference -import com.lambda.interaction.rotation.Rotation.Companion.dist -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.rotation.Rotation.Companion.wrap -import com.lambda.interaction.rotation.RotationRequest -import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces -import com.lambda.interaction.visibilty.VisibilityChecker.scanSurfaces +import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.angleDifference +import com.lambda.interaction.request.rotation.Rotation.Companion.dist +import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.Rotation.Companion.wrap +import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces +import com.lambda.interaction.request.rotation.visibilty.blockHit +import com.lambda.interaction.request.rotation.visibilty.lookAtHit import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.client.TaskFlowModule @@ -55,6 +57,7 @@ import com.lambda.util.player.MovementUtils.isInputting import com.lambda.util.player.MovementUtils.newMovementInput import com.lambda.util.player.MovementUtils.roundedForward import com.lambda.util.player.MovementUtils.roundedStrafing +import com.lambda.util.world.raycast.InteractionMask import com.lambda.util.world.raycast.RayCastUtils.blockResult import com.lambda.util.world.toFastVec import net.minecraft.util.Hand @@ -83,7 +86,7 @@ object Scaffold : Module( private val direction by setting("Direction", LookingDirection.FREE) { page == Page.ROTATION } private val optimalPitch by setting("Optimal Pitch", 81.0, 70.0..85.0, 0.05) { page == Page.ROTATION } - private val interactionConfig = InteractionSettings(this) { page == Page.INTERACTION } + private val interactionConfig = InteractionSettings(this, InteractionMask.BLOCK) { page == Page.INTERACTION } // Placement private var placeInfo: PlaceInfo? = null @@ -91,6 +94,8 @@ object Scaffold : Module( private var lastRotation: Rotation? = null private var edjeDistance = 0.0 + private var lastRequest: RotationRequest? = null + // Sneaking private var placeInfoAge = 0 private var sneakTicks = 0 @@ -123,14 +128,14 @@ object Scaffold : Module( } init { - rotate { - request { - lastRotation = null - val info = updatePlaceInfo() ?: return@request null - val rotation = rotate(info) ?: return@request null - - RotationRequest(rotation, rotationConfig) - } + onRotate { + lastRotation = null + lastRequest = null + val info = updatePlaceInfo() ?: return@onRotate + + lastRequest = lookAtHit( + blockHit(info.clickPos, info.clickSide, interactionConfig.placeReach) + ) { rotate(info) }.requestBy(rotationConfig) } listen { @@ -154,6 +159,7 @@ object Scaffold : Module( renderInfo.clear() keepLevel = null + lastRequest = null sneakTicks = 0 } } @@ -172,7 +178,7 @@ object Scaffold : Module( // Getting the latest block of the placement sequence placeInfo = buildPlaceInfo( basePos = BlockPos(player.pos.x.floorToInt(), y, player.pos.z.floorToInt()), - range = interactionConfig.reach + 2, + range = interactionConfig.placeReach + 2, sides = builderSideMask ) @@ -195,7 +201,7 @@ object Scaffold : Module( private fun SafeContext.rotate(info: PlaceInfo): Rotation? { val eye = player.eyePos - val reach = interactionConfig.reach + val reach = interactionConfig.placeReach val reachSq = reach.pow(2) val input = newMovementInput() @@ -217,7 +223,7 @@ object Scaffold : Module( // Dividing the surface by segments and iterating through them val pointScan = mutableSetOf().apply { val box = Box(info.clickPos) - val sides = if (TaskFlowModule.interact.visibilityCheck) { + val sides = if (TaskFlowModule.interact.checkSideVisibility) { box.getVisibleSurfaces(eye) } else Direction.entries.toSet() scanSurfaces( @@ -287,10 +293,10 @@ object Scaffold : Module( if (edjeDistance > 0 && edjeDistance < minPlaceDist) return // Raycast the rotation - var blockResult: BlockHitResult? = castRotation(currentRotation, info) + var blockResult: BlockHitResult? = lastRequest?.target?.hit?.hitIfValid()?.blockResult // Use fallback hit vec for nonstrict ac's - if (!interactionConfig.useRayCast && blockResult == null) { + if (!interactionConfig.strictRayCast && blockResult == null) { blockResult = BlockHitResult(info.hitVec, info.clickSide, info.clickPos, false) } @@ -367,7 +373,7 @@ object Scaffold : Module( // Checks if the rotation matches the placement requirements private fun castRotation(rotation: Rotation, info: PlaceInfo): BlockHitResult? { - val blockResult = rotation.rayCast(interactionConfig.reach)?.blockResult ?: return null + val blockResult = rotation.rayCast(interactionConfig.placeReach)?.blockResult ?: return null if (blockResult.blockPos != info.clickPos || blockResult.side != info.clickSide) return null return blockResult } diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt index b27fb7989..2ba776ca7 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt @@ -33,7 +33,7 @@ import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.buildWorldProjection import com.lambda.graphics.gl.Matrices.withVertexTransform import com.lambda.graphics.shader.Shader -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation import com.lambda.module.Module import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.client.GuiSettings.colorSpeed @@ -48,7 +48,7 @@ import com.lambda.util.math.lerp import com.lambda.util.math.multAlpha import com.lambda.util.math.transform import com.lambda.util.player.MovementUtils.moveDelta -import com.lambda.util.world.raycast.RayCastMask +import com.lambda.util.world.raycast.InteractionMask import net.minecraft.entity.Entity import net.minecraft.util.math.Vec3d import org.lwjgl.opengl.GL11.GL_ONE @@ -135,7 +135,7 @@ object Particles : Module( repeat(environmentSpawnAmount) { var particlePos = player.pos + Rotation(random(-180.0, 180.0), 0.0).vector * random(0.0, environmentRange) - Rotation.DOWN.rayCast(6.0, particlePos + UP * 2.0, true, RayCastMask.BLOCK)?.pos?.let { + Rotation.DOWN.rayCast(6.0, particlePos + UP * 2.0, true, InteractionMask.BLOCK)?.pos?.let { particlePos = it + UP * 0.03 } ?: return@repeat diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 2388f16e1..299b78055 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -19,14 +19,13 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.GoalBlock import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.RotationConfig +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.event.events.EntityEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.RotationManager.rotate import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock +import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -60,7 +59,6 @@ class BreakBlock @Ta5kBuilder constructor( private var drop: ItemEntity? = null private var state = State.BREAKING - private var isValid = false enum class State { BREAKING, COLLECTING @@ -80,18 +78,6 @@ class BreakBlock @Ta5kBuilder constructor( } init { - rotate { - request { - if (state != State.BREAKING) return@request null - if (!rotate || ctx.instantBreak) return@request null - - lookAtBlock(blockPos, rotation, interact, sides) - } - finished { context -> - isValid = context.isValid - } - } - listen { drop?.let { itemDrop -> if (!world.entities.contains(itemDrop)) { @@ -113,8 +99,12 @@ class BreakBlock @Ta5kBuilder constructor( return@listen } ?: BaritoneUtils.cancel() - if (isValid || !rotate || ctx.instantBreak) { - hitBlock(ctx.result.side) + if (rotate && !ctx.instantBreak && state == State.BREAKING) { + lookAtBlock(blockPos, sides, interact).let { + if (it.requestBy(rotation).done) { + hitBlock(ctx.result.side) + } + } } if (done()) { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 6856879fa..28bd488e3 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -20,13 +20,13 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.RotationConfig +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.event.events.MovementEvent import com.lambda.event.events.PacketEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.RotationManager.rotate +import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.DynamicBlueprint @@ -83,7 +83,7 @@ class BuildTask @Ta5kBuilder constructor( } currentPlacement?.let { context -> - if (!context.rotation.isValid) return@listen + if (!context.rotation.done) return@listen if (inScope++ < 1) return@listen // ToDo: Should not be needed but timings are wrong context.place(interact.swingHand) pendingPlacements.add(context) @@ -160,12 +160,13 @@ class BuildTask @Ta5kBuilder constructor( } } - rotate { - request { - if (currentPlacement == null) return@request null - if (!build.rotateForPlace) return@request null - currentPlacement?.rotation - } + onRotate { + if (currentPlacement == null) return@onRotate + if (!build.rotateForPlace) return@onRotate + + rotation.request( + currentPlacement?.rotation ?: return@onRotate + ) } listen { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 7336a7b8d..3e8cf7dec 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -18,11 +18,11 @@ package com.lambda.task.tasks import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.RotationConfig +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.event.events.InventoryEvent -import com.lambda.event.events.RotationEvent +import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock +import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.world.raycast.RayCastUtils.blockResult @@ -80,18 +80,14 @@ class OpenContainer @Ta5kBuilder constructor( } } - listen { event -> - if (!rotate) return@listen - event.request = lookAtBlock(blockPos, rotation, interact, sides) - } - - listen { - if (!rotate) return@listen + listen { if (state != State.SCOPING) return@listen - if (!it.request.isValid) return@listen - if (inScope++ >= interact.scopeThreshold) { - val hitResult = it.request.checkedResult?.blockResult ?: return@listen + val target = lookAtBlock(blockPos, sides, interact) + if (rotate && !target.requestBy(rotation).done) return@listen + + if (inScope++ >= TaskFlowModule.scopeThreshold) { + val hitResult = target.hit?.hitIfValid()?.blockResult ?: return@listen interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) state = State.OPENING diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index 390017ad6..a159fbd48 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -26,8 +26,11 @@ import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.PlaceContext -import com.lambda.interaction.rotation.Rotation.Companion.rotation -import com.lambda.interaction.rotation.RotationRequest +import com.lambda.interaction.request.rotation.Rotation.Companion.rotation +import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotation.RotationManager.onRotate +import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BlockUtils @@ -40,6 +43,7 @@ import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket class PlaceBlock @Ta5kBuilder constructor( private val ctx: PlaceContext, private val rotate: Boolean = TaskFlowModule.build.rotateForPlace, + private val rotation: RotationConfig = TaskFlowModule.rotation, private val interact: InteractionConfig = TaskFlowModule.interact, private val waitForConfirmation: Boolean = TaskFlowModule.build.placeConfirmation, ) : Task() { @@ -69,10 +73,7 @@ class PlaceBlock @Ta5kBuilder constructor( state = State.ROTATING } else { state = State.PRIME_ROTATION - primeContext = RotationRequest( - ctx.primeDirection.rotation, - ctx.rotation.config, - ) + primeContext = ctx.rotation } if (matches) { @@ -85,25 +86,26 @@ class PlaceBlock @Ta5kBuilder constructor( } init { - listen { event -> - if (!rotate) return@listen - event.request = if (state == State.PRIME_ROTATION) { - primeContext - } else ctx.rotation + onRotate { + rotation.request( + if (state == State.PRIME_ROTATION) { + primeContext ?: return@onRotate + } else ctx.rotation + ) } listen { event -> if (!rotate) return@listen - if (!event.request.isValid) return@listen + if (!event.request.done) return@listen + when (state) { State.PRIME_ROTATION -> { - if (event.request.rotation != primeContext?.rotation) return@listen + if (event.request.target != primeContext?.target) return@listen state = State.ROTATING } State.ROTATING -> { - if (event.request.rotation != ctx.rotation.rotation) return@listen - if (!event.request.isValid) return@listen + if (event.request.target != ctx.rotation.target) return@listen state = State.PLACING } diff --git a/common/src/main/kotlin/com/lambda/util/collections/CachedValue.kt b/common/src/main/kotlin/com/lambda/util/collections/CachedValue.kt new file mode 100644 index 000000000..870fd7664 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/collections/CachedValue.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.util.collections + +class CachedValue (private val initializer: () -> T) { + private var _value: T? = null + + val value: T? + get() { + if (_value == null) _value = initializer() + return _value + } + + fun update() { + _value = initializer() + } +} + +fun cached(initializer: () -> T) = CachedValue(initializer) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt b/common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt index 465939fcd..9359b099c 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt @@ -29,4 +29,6 @@ class ResettableLazy(private val initializer: () -> T) { fun reset() { _value = null } -} \ No newline at end of file +} + +fun resettableLazy(initializer: () -> T) = ResettableLazy(initializer) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/extension/Entity.kt b/common/src/main/kotlin/com/lambda/util/extension/Entity.kt index a58b76cb2..638986ffb 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/Entity.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/Entity.kt @@ -17,10 +17,8 @@ package com.lambda.util.extension -import com.lambda.interaction.rotation.Rotation -import com.lambda.util.math.lerp +import com.lambda.interaction.request.rotation.Rotation import net.minecraft.entity.Entity -import net.minecraft.entity.LivingEntity import net.minecraft.util.math.Vec3d val Entity.prevPos diff --git a/common/src/main/kotlin/com/lambda/util/math/Linear.kt b/common/src/main/kotlin/com/lambda/util/math/Linear.kt index f4fb37922..def44e228 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Linear.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Linear.kt @@ -17,7 +17,7 @@ package com.lambda.util.math -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation import net.minecraft.util.math.Box import net.minecraft.util.math.Vec3d import java.awt.Color diff --git a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt index 7231c501d..d3eeb33d6 100644 --- a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt @@ -18,7 +18,7 @@ package com.lambda.util.player import com.lambda.context.SafeContext -import com.lambda.interaction.RotationManager +import com.lambda.interaction.request.rotation.RotationManager import com.lambda.util.math.MathUtils.toDegree import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.MathUtils.toRadian diff --git a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt index 7ce175c09..4e7438e64 100644 --- a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt +++ b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionEntity.kt @@ -19,7 +19,7 @@ package com.lambda.util.player.prediction import com.lambda.Lambda.mc import com.lambda.context.SafeContext -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.flooredPos diff --git a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt index 972f50133..3b8f66d0c 100644 --- a/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt +++ b/common/src/main/kotlin/com/lambda/util/player/prediction/PredictionTick.kt @@ -17,7 +17,7 @@ package com.lambda.util.player.prediction -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation import net.minecraft.util.math.Box import net.minecraft.util.math.Vec3d diff --git a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastMask.kt b/common/src/main/kotlin/com/lambda/util/world/raycast/InteractionMask.kt similarity index 91% rename from common/src/main/kotlin/com/lambda/util/world/raycast/RayCastMask.kt rename to common/src/main/kotlin/com/lambda/util/world/raycast/InteractionMask.kt index c9fa40a47..070d29ab8 100644 --- a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastMask.kt +++ b/common/src/main/kotlin/com/lambda/util/world/raycast/InteractionMask.kt @@ -17,7 +17,7 @@ package com.lambda.util.world.raycast -enum class RayCastMask(val block: Boolean, val entity: Boolean) { +enum class InteractionMask(val block: Boolean, val entity: Boolean) { BOTH(true, true), BLOCK(true, false), ENTITY(false, true) diff --git a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt index 7bf55c02e..f3f202f6f 100644 --- a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt @@ -19,7 +19,7 @@ package com.lambda.util.world.raycast import com.lambda.Lambda.mc import com.lambda.context.SafeContext -import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation import com.lambda.threading.runSafe import com.lambda.util.math.distSq import net.minecraft.client.network.ClientPlayerEntity @@ -42,7 +42,7 @@ object RayCastUtils { start: Vec3d, direction: Vec3d, reach: Double, - mask: RayCastMask, + mask: InteractionMask, fluids: Boolean = false, ): HitResult? { val vec = direction.multiply(reach) @@ -73,7 +73,7 @@ object RayCastUtils { // ToDo: Should rather move player hitbox down and check collision fun distanceToGround(maxDist: Double = 100.0) = runSafe { val pos = player.pos.add(0.0, 0.1, 0.0) - val cast = Rotation.DOWN.rayCast(maxDist, pos, false, RayCastMask.BLOCK) ?: return@runSafe maxDist + val cast = Rotation.DOWN.rayCast(maxDist, pos, false, InteractionMask.BLOCK) ?: return@runSafe maxDist return@runSafe max(0.0, pos.y - cast.pos.y) } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index db3df6d09..bb35ed9b5 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -27,6 +27,7 @@ accessible method net/minecraft/entity/Entity setFlag (IZ)V accessible field net/minecraft/client/network/AbstractClientPlayerEntity playerListEntry Lnet/minecraft/client/network/PlayerListEntry; accessible field net/minecraft/entity/LivingEntity jumpingCooldown I accessible field net/minecraft/entity/Entity pos Lnet/minecraft/util/math/Vec3d; +accessible field net/minecraft/client/network/ClientPlayerInteractionManager lastSelectedSlot I # Camera accessible method net/minecraft/client/render/Camera setPos (DDD)V diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index aac98e584..dbc6369e0 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -54,5 +54,8 @@ ], "injectors": { "defaultRequire": 1 - } + }, + "mixins": [ + "entity.PlayerInventoryMixin" + ] } From 6c9993a11d5f7ab99caf7e07462ec31237e30372 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 4 Feb 2025 19:12:28 +0100 Subject: [PATCH 44/69] Remove deprecated rotation fix and fix wrong hit check --- .../construction/simulation/BuildSimulator.kt | 17 +++++++---------- .../module/modules/client/TaskFlowModule.kt | 14 -------------- .../com/lambda/task/tasks/OpenContainer.kt | 8 +++----- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 1ba80903d..70e82c28e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -31,7 +31,6 @@ import com.lambda.interaction.material.container.ContainerManager.findBestAvaila import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.interaction.request.rotation.visibilty.RequestedHit import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces @@ -55,7 +54,6 @@ import net.minecraft.item.ItemUsageContext import net.minecraft.registry.RegistryKeys import net.minecraft.state.property.Properties import net.minecraft.util.Hand -import net.minecraft.util.hit.HitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction @@ -152,7 +150,7 @@ object BuildSimulator { hit.blockResult?.blockPos == hitPos && hit.blockResult?.side == hitSide } - val checkedHits = mutableListOf() + val validHits = mutableListOf() val misses = mutableSetOf() val reachSq = reach.pow(2) @@ -175,11 +173,11 @@ object BuildSimulator { val checked = CheckedHit(hit, newRotation, reach) if (!checked.verify()) return@scanSurfaces - checkedHits.add(checked) + validHits.add(checked) } } - if (checkedHits.isEmpty()) { + if (validHits.isEmpty()) { if (misses.isNotEmpty()) { acc.add(BuildResult.OutOfReach(pos, eye, misses)) return@forEach @@ -189,7 +187,7 @@ object BuildSimulator { return@forEach } - interact.pointSelection.select(checkedHits)?.let { checkedHit -> + interact.pointSelection.select(validHits)?.let { checkedHit -> val optimalStack = target.getStack(world, pos) // ToDo: For each hand and sneak or not? @@ -379,7 +377,6 @@ object BuildSimulator { hit.blockResult?.blockPos == pos } - val checkedHits = mutableListOf() /* the player is buried inside the block */ if (boxes.any { it.contains(eye) }) { currentCast?.blockResult?.let { blockHit -> @@ -400,7 +397,7 @@ object BuildSimulator { } } - val validHits = mutableMapOf() + val validHits = mutableListOf() val misses = mutableSetOf() val reachSq = reach.pow(2) @@ -417,7 +414,7 @@ object BuildSimulator { val checked = CheckedHit(hit, newRotation, reach) if (!checked.verify()) return@scanSurfaces - checkedHits.add(checked) + validHits.add(checked) } } @@ -427,7 +424,7 @@ object BuildSimulator { return acc } - interact.pointSelection.select(checkedHits)?.let { checkedHit -> + interact.pointSelection.select(validHits)?.let { checkedHit -> val blockHit = checkedHit.hit.blockResult ?: return@let val breakContext = BreakContext( diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt index ac1b56d33..d381e52e5 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlowModule.kt @@ -42,20 +42,6 @@ object TaskFlowModule : Module( val build = BuildSettings(this) { page == Page.BUILD } val rotation = RotationSettings(this) { page == Page.ROTATION } val interact = InteractionSettings(this, InteractionMask.BOTH) { page == Page.INTERACTION } - - // ToDo: remove - // might be useless since grim has ping compensation things and shit - // its better to make sure the interactions are 100% matching the minecraft's ones - private val pingTimeout by setting("Ping Timeout", false, "Timeout on high ping") { page == Page.INTERACTION } - private val inScopeThreshold by setting("Constant Timeout", 1, 0..20, 1, "How many ticks to wait after target box is in rotation scope"," ticks") { - page == Page.INTERACTION && !pingTimeout - } - - val scopeThreshold: Int - get() = - if (pingTimeout) PingManager.lastPing.toInt() / 50 - else inScopeThreshold - val inventory = InventorySettings(this) { page == Page.INVENTORY } val showAllEntries by setting("Show All Entries", false, "Show all entries in the task tree") { page == Page.DEBUG } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 3e8cf7dec..3ef9d19bf 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -86,12 +86,10 @@ class OpenContainer @Ta5kBuilder constructor( val target = lookAtBlock(blockPos, sides, interact) if (rotate && !target.requestBy(rotation).done) return@listen - if (inScope++ >= TaskFlowModule.scopeThreshold) { - val hitResult = target.hit?.hitIfValid()?.blockResult ?: return@listen - interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) + val hitResult = target.hit?.hitIfValid()?.blockResult ?: return@listen + interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) - state = State.OPENING - } + state = State.OPENING } } } From e38cdb372713c376756976c4d7272b0a6be5bd56 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 4 Feb 2025 19:15:44 +0100 Subject: [PATCH 45/69] Use proper update event --- .../transfer/transaction/PickFromInventoryTransaction.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt index eb592261f..5c8e7d429 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/transaction/PickFromInventoryTransaction.kt @@ -36,7 +36,7 @@ class PickFromInventoryTransaction @Ta5kBuilder constructor( confirming = true } - listen { + listen { finish() } } From b7c9113dde03ceba00283a807d8c54501798f9d8 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 4 Feb 2025 20:22:52 +0100 Subject: [PATCH 46/69] Make requests data classes so we can debug them better --- .../material/transfer/SlotTransfer.kt | 2 +- .../request/rotation/RotationRequest.kt | 6 +++--- .../rotation/visibilty/RequestedHit.kt | 21 ++++++++++++------- .../rotation/visibilty/RotationTarget.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 11 +++------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt index fffc3a84c..b93326f3a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt @@ -56,7 +56,7 @@ class SlotTransfer @Ta5kBuilder constructor( listen { val current = player.currentScreenHandler if (current != screen) { - failure("Screen has changed. Expected ${screen::class.simpleName} (revision ${screen.revision}, got ${current::class.simpleName} (revision ${current.revision})") + failure("Screen has changed. Expected ${screen::class.simpleName} (revision ${screen.revision}) but got ${current::class.simpleName} (revision ${current.revision})") return@listen } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index fcda63935..ed15d96ff 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -22,15 +22,15 @@ import com.lambda.interaction.request.Request import com.lambda.interaction.request.rotation.visibilty.RotationTarget import com.lambda.threading.runSafe -class RotationRequest( +data class RotationRequest( val target: RotationTarget, - priority: Priority, + val prio: Priority, val mode: RotationMode, var keepTicks: Int = 3, var decayTicks: Int = 0, val turnSpeed: () -> Double = { 180.0 }, val speedMultiplier: Double = 1.0 -) : Request(priority) { +) : Request(prio) { constructor( target: RotationTarget, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt index 2d8ecba72..c1077abaa 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RequestedHit.kt @@ -33,10 +33,9 @@ import net.minecraft.util.math.Direction /** * An abstract class representing a rotation target, which can be either a block or an entity. */ -abstract class RequestedHit private constructor( - val sides: Set, - val reach: Double -) { +abstract class RequestedHit { + abstract val sides: Set + abstract val reach: Double /** * Verifies if the given [HitResult] satisfies the criteria of this @@ -80,8 +79,11 @@ abstract class RequestedHit private constructor( * @param entity The [LivingEntity] to be hit. * @param reach The maximum distance for the hit. */ - class Entity(val entity: LivingEntity, reach: Double) : RequestedHit(ALL_SIDES, reach) { - + data class Entity( + val entity: LivingEntity, + override val reach: Double, + override val sides: Set = ALL_SIDES + ) : RequestedHit() { override fun getBoundingBoxes() = listOf(entity.boundingBox) @@ -96,8 +98,11 @@ abstract class RequestedHit private constructor( * @param sides The set of directions for which the hit is requested. * @param reach The maximum distance for the hit. */ - class Block(val blockPos: BlockPos, sides: Set, reach: Double) : RequestedHit(sides, reach) { - + data class Block( + val blockPos: BlockPos, + override val sides: Set, + override val reach: Double + ) : RequestedHit() { override fun getBoundingBoxes(): List = runSafe { val state = blockPos.blockState(world) val voxelShape = state.getOutlineShape(world, blockPos) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index d50dd02a4..baf12bf97 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -31,7 +31,7 @@ import com.lambda.util.collections.cached * @param verify A lambda to check the active rotation. * @param buildRotation A lambda that builds the rotation. */ -class RotationTarget( +data class RotationTarget( val hit: RequestedHit? = null, val verify: RotationTarget.() -> Boolean = { hit?.verifyRotation() ?: true }, private val buildRotation: SafeContext.() -> Rotation?, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 28bd488e3..d9d1b34b5 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -66,7 +66,6 @@ class BuildTask @Ta5kBuilder constructor( private var currentPlacement: PlaceContext? = null private var placements = 0 private var breaks = 0 - private var inScope = 0 override fun SafeContext.onStart() { (blueprint as? DynamicBlueprint)?.create() @@ -83,12 +82,10 @@ class BuildTask @Ta5kBuilder constructor( } currentPlacement?.let { context -> - if (!context.rotation.done) return@listen - if (inScope++ < 1) return@listen // ToDo: Should not be needed but timings are wrong + if (build.rotateForPlace && !context.rotation.done) return@listen context.place(interact.swingHand) pendingPlacements.add(context) currentPlacement = null - inScope = 0 } (blueprint as? DynamicBlueprint)?.update() @@ -161,12 +158,10 @@ class BuildTask @Ta5kBuilder constructor( } onRotate { - if (currentPlacement == null) return@onRotate if (!build.rotateForPlace) return@onRotate + val rotateTo = currentPlacement?.rotation ?: return@onRotate - rotation.request( - currentPlacement?.rotation ?: return@onRotate - ) + rotation.request(rotateTo) } listen { From ebe0bd446be4746c5fb1d00019ae2e2dede8a15a Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 02:47:12 +0100 Subject: [PATCH 47/69] Configuration delegation --- .../lambda/mixin/render/DebugHudMixin.java | 4 +- .../lambda/command/commands/BuildCommand.kt | 2 +- .../lambda/command/commands/TaskCommand.kt | 8 +- .../command/commands/TransferCommand.kt | 2 +- .../com/lambda/config/groups/BuildSettings.kt | 35 +++-- .../lambda/config/groups/InteractionConfig.kt | 2 +- .../config/groups/InteractionSettings.kt | 12 +- .../lambda/config/groups/InventoryConfig.kt | 12 +- .../lambda/config/groups/InventorySettings.kt | 4 +- .../lambda/config/groups/RotationSettings.kt | 4 +- .../blueprint/DynamicBlueprint.kt | 2 +- .../construction/result/BreakResult.kt | 8 +- .../construction/result/BuildResult.kt | 7 +- .../construction/simulation/BuildSimulator.kt | 125 ++++++++---------- .../material/container/ContainerManager.kt | 21 +-- .../interaction/request/RequestHandler.kt | 10 +- .../request/rotation/RotationConfig.kt | 10 +- .../request/rotation/RotationManager.kt | 22 +-- .../request/rotation/RotationMode.kt | 16 +-- .../request/rotation/RotationRequest.kt | 12 +- .../rotation/visibilty/RotationTarget.kt | 4 +- .../rotation/visibilty/RotationTargets.kt | 26 ++-- .../com/lambda/module/hud/TaskFlowHUD.kt | 4 +- .../module/modules/debug/ContainerTest.kt | 2 +- .../lambda/module/modules/movement/Speed.kt | 2 +- .../lambda/module/modules/player/Freecam.kt | 5 +- .../module/modules/player/HighwayTools.kt | 60 ++++++--- .../module/modules/player/InventoryTweaks.kt | 2 +- .../com/lambda/module/modules/player/Nuker.kt | 2 +- .../lambda/module/modules/player/Replay.kt | 6 +- .../lambda/module/modules/player/Scaffold.kt | 8 +- .../module/modules/player/WorldEater.kt | 2 +- .../lambda/task/{TaskFlow.kt => RootTask.kt} | 6 +- .../src/main/kotlin/com/lambda/task/Task.kt | 8 +- .../com/lambda/task/tasks/AcquireMaterial.kt | 7 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 23 ++-- .../lambda/util/collections/ResettableLazy.kt | 34 ----- .../{CachedValue.kt => UpdatableLazy.kt} | 4 +- 38 files changed, 247 insertions(+), 276 deletions(-) rename common/src/main/kotlin/com/lambda/task/{TaskFlow.kt => RootTask.kt} (90%) delete mode 100644 common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt rename common/src/main/kotlin/com/lambda/util/collections/{CachedValue.kt => UpdatableLazy.kt} (87%) diff --git a/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java b/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java index 2d256d60b..883924688 100644 --- a/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java @@ -17,7 +17,7 @@ package com.lambda.mixin.render; -import com.lambda.task.TaskFlow; +import com.lambda.task.RootTask; import com.lambda.util.DebugInfoHud; import net.minecraft.client.gui.hud.DebugHud; import org.spongepowered.asm.mixin.Mixin; @@ -36,6 +36,6 @@ private void onGetRightText(CallbackInfoReturnable> cir) { @Inject(method = "getLeftText", at = @At("TAIL")) private void onGetLeftText(CallbackInfoReturnable> cir) { - cir.getReturnValue().addAll(List.of(TaskFlow.INSTANCE.toString().split("\n"))); + cir.getReturnValue().addAll(List.of(RootTask.INSTANCE.toString().split("\n"))); } } diff --git a/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt index 4f1cfecf4..12a1dcb87 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt @@ -28,7 +28,7 @@ import com.lambda.command.LambdaCommand import com.lambda.interaction.construction.StructureRegistry import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint -import com.lambda.task.TaskFlow.run +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.threading.runSafe diff --git a/common/src/main/kotlin/com/lambda/command/commands/TaskCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TaskCommand.kt index 97c33383a..c4aa27a48 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TaskCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TaskCommand.kt @@ -21,7 +21,7 @@ import com.lambda.brigadier.argument.literal import com.lambda.brigadier.execute import com.lambda.brigadier.required import com.lambda.command.LambdaCommand -import com.lambda.task.TaskFlow +import com.lambda.task.RootTask import com.lambda.util.Communication.info import com.lambda.util.extension.CommandBuilder @@ -34,15 +34,15 @@ object TaskCommand : LambdaCommand( required(literal("cancel")) { execute { this@TaskCommand.info("Cancelling all tasks") - TaskFlow.cancel() + RootTask.cancel() } } required(literal("clear")) { execute { this@TaskCommand.info("Clearing all tasks") - TaskFlow.cancel() - TaskFlow.clear() + RootTask.cancel() + RootTask.clear() } } } diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index 3f9eeef79..367c288c8 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -28,7 +28,7 @@ import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ContainerManager.containerWithMaterial import com.lambda.interaction.material.container.ContainerManager.containerWithSpace import com.lambda.interaction.material.transfer.TransferResult -import com.lambda.task.TaskFlow.run +import com.lambda.task.RootTask.run import com.lambda.util.Communication.info import com.lambda.util.extension.CommandBuilder diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 89a6deabf..cfa945eb9 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -18,7 +18,6 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.module.modules.client.TaskFlowModule.setting import com.lambda.util.BlockUtils.allSigns class BuildSettings( @@ -26,29 +25,29 @@ class BuildSettings( vis: () -> Boolean = { true } ) : BuildConfig { enum class Page { - GENERAL, BREAK, PLACE + General, Break, Place } - private val page by c.setting("Build Page", Page.GENERAL, "Current page", vis) + private val page by c.setting("Build Page", Page.General, "Current page", vis) // General - override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.GENERAL } - override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks") { vis() && page == Page.GENERAL && pathing } - override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.GENERAL } + override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.General } + override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks") { vis() && page == Page.General && pathing } + override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.General } // Breaking - override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() && page == Page.BREAK } - override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation") { vis() && page == Page.BREAK } - override val maxPendingBreaks by c.setting("Max Pending Breaks", 1, 1..10, 1, "Maximum pending block breaks") { vis() && page == Page.BREAK } - override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.BREAK } - override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.BREAK } - override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.BREAK } - override val ignoredBlocks by setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.BREAK } + override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() && page == Page.Break } + override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation") { vis() && page == Page.Break } + override val maxPendingBreaks by c.setting("Max Pending Breaks", 1, 1..10, 1, "Maximum pending block breaks") { vis() && page == Page.Break } + override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.Break } + override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.Break } + override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.Break } + override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() && page == Page.Break } // Placing - override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() && page == Page.PLACE } - override val placeConfirmation by c.setting("Place Confirmation", true, "Wait for block placement confirmation") { vis() && page == Page.PLACE } - override val placeTimeout by c.setting("Place Timeout", 10, 1..30, 1, "Timeout for block placement in ticks", unit = " ticks") { vis() && page == Page.PLACE && placeConfirmation } - override val maxPendingPlacements by c.setting("Max Pending Places", 1, 1..10, 1, "Maximum pending block places") { vis() && page == Page.PLACE } - override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() && page == Page.PLACE } + override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() && page == Page.Place } + override val placeConfirmation by c.setting("Place Confirmation", true, "Wait for block placement confirmation") { vis() && page == Page.Place } + override val placeTimeout by c.setting("Place Timeout", 10, 1..30, 1, "Timeout for block placement in ticks", unit = " ticks") { vis() && page == Page.Place && placeConfirmation } + override val maxPendingPlacements by c.setting("Max Pending Places", 1, 1..10, 1, "Maximum pending block places") { vis() && page == Page.Place } + override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() && page == Page.Place } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt index 92b260fcc..04e28b4c2 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt @@ -28,7 +28,7 @@ interface InteractionConfig { /** * Maximum block interaction distance */ - val placeReach: Double + val interactReach: Double /** * Maximum possible interaction distance diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index 389573b81..0ba046c84 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -30,7 +30,7 @@ class InteractionSettings( // Reach private val useDefaultReach by c.setting("Default Reach", true, "Whether to use vanilla interaction ranges", vis) private val attackReachSetting = if (usage.entity) c.setting("Attack Reach", DEFAULT_ATTACK_REACH, 1.0..10.0, 0.01, "Maximum entity interaction distance") { vis() && !useDefaultReach } else null - private val placeReachSetting = if (usage.block) c.setting("Place Reach", DEFAULT_PLACE_REACH, 1.0..10.0, 0.01, "Maximum block interaction distance") { vis() && !useDefaultReach } else null + private val interactReachSetting = if (usage.block) c.setting("Interact Reach", DEFAULT_INTERACT_REACH, 1.0..10.0, 0.01, "Maximum block interaction distance") { vis() && !useDefaultReach } else null override val attackReach: Double get() { check(usage.entity) { @@ -40,18 +40,18 @@ class InteractionSettings( return if (useDefaultReach) DEFAULT_ATTACK_REACH else attackReachSetting!!.value } - override val placeReach: Double get() { + override val interactReach: Double get() { check(usage.block) { "Given interaction config has no place reach implementation" } - return if (useDefaultReach) DEFAULT_ATTACK_REACH else placeReachSetting!!.value + return if (useDefaultReach) DEFAULT_INTERACT_REACH else interactReachSetting!!.value } override val scanReach: Double get() = when (usage) { InteractionMask.ENTITY -> attackReach - InteractionMask.BLOCK -> placeReach - InteractionMask.BOTH -> max(attackReach, placeReach) + InteractionMask.BLOCK -> interactReach + InteractionMask.BOTH -> max(attackReach, interactReach) } // Point scan @@ -65,6 +65,6 @@ class InteractionSettings( companion object { const val DEFAULT_ATTACK_REACH = 3.0 - const val DEFAULT_PLACE_REACH = 4.5 + const val DEFAULT_INTERACT_REACH = 4.5 } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt index 26c04d38e..2fc413450 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventoryConfig.kt @@ -32,27 +32,27 @@ interface InventoryConfig { val storePriority: Priority enum class Priority { - WITH_MIN_ITEMS, - WITH_MAX_ITEMS; + WithMinItems, + WithMaxItems; fun materialComparator(selection: StackSelection) = when (this) { - WITH_MAX_ITEMS -> compareBy { it.rank } + WithMaxItems -> compareBy { it.rank } .thenByDescending { it.materialAvailable(selection) } .thenBy { it.name } - WITH_MIN_ITEMS -> compareBy { it.rank } + WithMinItems -> compareBy { it.rank } .thenBy { it.materialAvailable(selection) } .thenBy { it.name } } fun spaceComparator(selection: StackSelection) = when (this) { - WITH_MAX_ITEMS -> compareBy { it.rank } + WithMaxItems -> compareBy { it.rank } .thenByDescending { it.spaceAvailable(selection) } .thenBy { it.name } - WITH_MIN_ITEMS -> compareBy { it.rank } + WithMinItems -> compareBy { it.rank } .thenBy { it.spaceAvailable(selection) } .thenBy { it.name } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index 74f962802..01dfc1a76 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -28,6 +28,6 @@ class InventorySettings( override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest", vis) override val actionTimout by c.setting("Action Timeout", 10, 0..100, 1, "How long to wait for after each inventory action", " ticks", vis) override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones", vis) - override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WITH_MIN_ITEMS, "What container to prefer when retrieving the item from", vis) - override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WITH_MIN_ITEMS, "What container to prefer when storing the item to", vis) + override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from", vis) + override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index c9cc188d7..129490058 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -19,10 +19,8 @@ package com.lambda.config.groups import com.lambda.config.Configurable import com.lambda.interaction.request.Priority -import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationMode -import com.lambda.interaction.request.rotation.RotationRequest import kotlin.math.* import kotlin.random.Random @@ -31,7 +29,7 @@ class RotationSettings( priority: Priority = 0, vis: () -> Boolean = { true } ) : RotationConfig(priority) { - override var rotationMode by c.setting("Mode", RotationMode.SYNC, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", vis) + override var rotationMode by c.setting("Mode", RotationMode.Sync, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera, NONE - No rotation", vis) /** How many ticks to keep the rotation before resetting */ override val keepTicks by c.setting("Keep Rotation", 3, 0..10, 1, "Ticks to keep rotation", " ticks") { rotate && vis() } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/DynamicBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/DynamicBlueprint.kt index e121aaf91..df9523f26 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/DynamicBlueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/DynamicBlueprint.kt @@ -41,7 +41,7 @@ data class DynamicBlueprint( override var structure: Structure = emptyMap() private set(value) { field = value - bounds.reset() + bounds.update() } override fun toString() = "Dynamic Blueprint at ${center?.toShortString()}" diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index c1cb63755..47749be3c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.material.StackSelection.Companion.select @@ -97,6 +98,7 @@ sealed class BreakResult : BuildResult() { override val blockPos: BlockPos, val blockState: BlockState, val badItem: Item, + val inventory: InventoryConfig ) : Drawable, Resolvable, BreakResult() { override val rank = Rank.BREAK_ITEM_CANT_MINE private val color = Color(255, 0, 0, 100) @@ -104,12 +106,12 @@ sealed class BreakResult : BuildResult() { override val pausesParent get() = true override fun resolve() = - findBestAvailableTool(blockState) + findBestAvailableTool(blockState, inventory = inventory) ?.select() - ?.transfer(MainHandContainer) + ?.transfer(MainHandContainer, inventory) ?: selectStack { isItem(badItem).not() - }.transfer(MainHandContainer) + }.transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing("Couldn't find a tool for ${blockState.block.name.string} with $badItem in main hand.") override fun SafeContext.buildRenderer() { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 59ac1bca9..972e01aea 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.construction.result import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalNear +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.material.StackSelection.Companion.select @@ -192,6 +193,7 @@ abstract class BuildResult : ComparableResult, Nameable { val context: BuildContext, val neededItem: Item, val currentItem: ItemStack, + val inventory: InventoryConfig ) : Drawable, Resolvable, BuildResult() { override val name: String get() = "Wrong item ($currentItem) for ${blockPos.toShortString()} need ${neededItem.name.string}" override val rank = Rank.WRONG_ITEM @@ -200,7 +202,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = neededItem.select() - .transfer(MainHandContainer) ?: MaterialContainer.Nothing() + .transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing() override fun SafeContext.buildRenderer() { if (blockPos.blockState(world).isAir) { @@ -227,6 +229,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val blockPos: BlockPos, val context: BuildContext, val neededStack: ItemStack, + val inventory: InventoryConfig ) : Drawable, Resolvable, BuildResult() { override val name: String get() = "Wrong stack for $blockPos need $neededStack." override val rank = Rank.WRONG_ITEM @@ -235,7 +238,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = - neededStack.select().transfer(MainHandContainer) ?: MaterialContainer.Nothing() + neededStack.select().transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing() override fun SafeContext.buildRenderer() { if (blockPos.blockState(world).isAir) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 70e82c28e..cf24bf59b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -17,8 +17,9 @@ package com.lambda.interaction.construction.simulation +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext -import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.PlaceContext @@ -30,6 +31,8 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.container.ContainerManager.findBestAvailableTool import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces @@ -62,27 +65,33 @@ import kotlin.jvm.optionals.getOrNull import kotlin.math.pow object BuildSimulator { - fun Blueprint.simulate(eye: Vec3d, reach: Double = TaskFlowModule.interact.placeReach) = - runSafe { - structure.entries.flatMap { (pos, target) -> - checkRequirements(pos, target)?.let { - return@flatMap setOf(it) - } - checkPlaceResults(pos, target, eye, reach).let { - if (it.isEmpty()) return@let - return@flatMap it - } - checkBreakResults(pos, eye, reach).let { - if (it.isEmpty()) return@let - return@flatMap it - } - warn("Nothing matched $pos $target") - emptySet() - }.toSet() - } ?: emptySet() + fun Blueprint.simulate( + eye: Vec3d, + interact: InteractionConfig = TaskFlowModule.interact, + rotation: RotationConfig = TaskFlowModule.rotation, + inventory: InventoryConfig = TaskFlowModule.inventory, + ) = runSafe { + structure.entries.flatMap { (pos, target) -> + checkRequirements(pos, target)?.let { + return@flatMap setOf(it) + } + checkPlaceResults(pos, target, eye, interact, rotation, inventory).let { + if (it.isEmpty()) return@let + return@flatMap it + } + checkBreakResults(pos, eye, interact, rotation, inventory).let { + if (it.isEmpty()) return@let + return@flatMap it + } + warn("Nothing matched $pos $target") + emptySet() + }.toSet() + } ?: emptySet() - private fun SafeContext.checkRequirements(pos: BlockPos, target: TargetState): BuildResult? { - /* the chunk is not loaded */ + private fun SafeContext.checkRequirements( + pos: BlockPos, + target: TargetState, + ): BuildResult? {/* the chunk is not loaded */ if (!world.isChunkLoaded(pos)) { return BuildResult.ChunkNotLoaded(pos) } @@ -126,16 +135,15 @@ object BuildSimulator { pos: BlockPos, target: TargetState, eye: Vec3d, - reach: Double, + interact: InteractionConfig, + rotation: RotationConfig, + inventory: InventoryConfig ): Set { val acc = mutableSetOf() val targetPosState = pos.blockState(world) if (target.isAir() || !targetPosState.isReplaceable) return acc - val interact = TaskFlowModule.interact - val rotation = TaskFlowModule.rotation - val preprocessing = target.findProcessorForState() preprocessing.sides.forEach { neighbor -> @@ -152,7 +160,7 @@ object BuildSimulator { val validHits = mutableListOf() val misses = mutableSetOf() - val reachSq = reach.pow(2) + val reachSq = interact.interactReach.pow(2) boxes.forEach { box -> val sides = if (interact.checkSideVisibility) { @@ -169,8 +177,8 @@ object BuildSimulator { val newRotation = eye.rotationTo(vec) - val hit = newRotation.rayCast(reach, eye) ?: return@scanSurfaces - val checked = CheckedHit(hit, newRotation, reach) + val hit = newRotation.rayCast(interact.interactReach, eye) ?: return@scanSurfaces + val checked = CheckedHit(hit, newRotation, interact.interactReach) if (!checked.verify()) return@scanSurfaces validHits.add(checked) @@ -204,9 +212,7 @@ object BuildSimulator { checkedResult.blockResult, ) val cachePos = CachedBlockPosition( - usageContext.world, - usageContext.blockPos, - false + usageContext.world, usageContext.blockPos, false ) val canBePlacedOn = optimalStack.canPlaceOn( usageContext.world.registryManager.get(RegistryKeys.BLOCK), @@ -265,17 +271,14 @@ object BuildSimulator { val hitBlock = blockHit.blockPos.blockState(world).block val shouldSneak = hitBlock in BlockUtils.interactionBlacklist - val primeDirection = (target as? TargetState.State) - ?.blockState - ?.getOrEmpty(Properties.HORIZONTAL_FACING) - ?.getOrNull() + val primeDirection = + (target as? TargetState.State)?.blockState?.getOrEmpty(Properties.HORIZONTAL_FACING)?.getOrNull() val placeContext = PlaceContext( eye, blockHit, RotationRequest( - lookAtBlock(blockHit.blockPos, setOf(blockHit.side), interact), - rotation + lookAtBlock(blockHit.blockPos, setOf(blockHit.side), interact), rotation ), eye.distanceTo(blockHit.pos), resultState, @@ -290,12 +293,12 @@ object BuildSimulator { val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) if (target is TargetState.Stack && !target.itemStack.equal(currentHandStack)) { - acc.add(BuildResult.WrongStack(pos, placeContext, target.itemStack)) + acc.add(BuildResult.WrongStack(pos, placeContext, target.itemStack, inventory)) return@forEach } if (optimalStack.item != currentHandStack.item) { - acc.add(BuildResult.WrongItem(pos, placeContext, optimalStack.item, currentHandStack)) + acc.add(BuildResult.WrongItem(pos, placeContext, optimalStack.item, currentHandStack, inventory)) return@forEach } @@ -309,7 +312,9 @@ object BuildSimulator { private fun SafeContext.checkBreakResults( pos: BlockPos, eye: Vec3d, - reach: Double, + interact: InteractionConfig, + rotation: RotationConfig, + inventory: InventoryConfig ): Set { val acc = mutableSetOf() val state = pos.blockState(world) @@ -333,7 +338,7 @@ object BuildSimulator { /* liquid needs to be submerged first to be broken */ if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid, eye, reach) + val submerge = checkPlaceResults(pos, TargetState.Solid, eye, interact, rotation, inventory) acc.add(BreakResult.Submerge(pos, state, submerge)) acc.addAll(submerge) return acc @@ -347,7 +352,7 @@ object BuildSimulator { if (adjacentLiquids.isNotEmpty()) { acc.add(BreakResult.BlockedByLiquid(pos, state)) adjacentLiquids.forEach { - val submerge = checkPlaceResults(pos.offset(it), TargetState.Solid, eye, reach) + val submerge = checkPlaceResults(pos.offset(it), TargetState.Solid, eye, interact, rotation, inventory) acc.addAll(submerge) } return acc @@ -358,14 +363,12 @@ object BuildSimulator { val stack = player.getStackInHand(it) if (stack.isEmpty) return@forEach if (stack.item.canMine(state, world, pos, player)) return@forEach - acc.add(BreakResult.ItemCantMine(pos, state, stack.item)) + acc.add(BreakResult.ItemCantMine(pos, state, stack.item, inventory)) return acc } - val interact = TaskFlowModule.interact - val rotation = TaskFlowModule.rotation val currentRotation = RotationManager.currentRotation - val currentCast = currentRotation.rayCast(reach, eye) + val currentCast = currentRotation.rayCast(interact.interactReach, eye) val voxelShape = state.getOutlineShape(world, pos) voxelShape.getClosestPointTo(eye).ifPresent { @@ -381,16 +384,10 @@ object BuildSimulator { if (boxes.any { it.contains(eye) }) { currentCast?.blockResult?.let { blockHit -> val rotationRequest = RotationRequest( - lookAtBlock(pos, interactionConfig = interact), - rotation + lookAtBlock(pos, config = interact), rotation ) val breakContext = BreakContext( - eye, - blockHit, - rotationRequest, - state, - player.activeHand, - instantBreakable(state, pos) + eye, blockHit, rotationRequest, state, player.activeHand, instantBreakable(state, pos) ) acc.add(BreakResult.Break(pos, breakContext)) return acc @@ -399,7 +396,7 @@ object BuildSimulator { val validHits = mutableListOf() val misses = mutableSetOf() - val reachSq = reach.pow(2) + val reachSq = interact.interactReach.pow(2) boxes.forEach { box -> scanSurfaces(box, Direction.entries.toSet(), interact.resolution) { side, vec -> @@ -410,8 +407,8 @@ object BuildSimulator { val newRotation = eye.rotationTo(vec) - val hit = newRotation.rayCast(reach, eye) ?: return@scanSurfaces - val checked = CheckedHit(hit, newRotation, reach) + val hit = newRotation.rayCast(interact.interactReach, eye) ?: return@scanSurfaces + val checked = CheckedHit(hit, newRotation, interact.interactReach) if (!checked.verify()) return@scanSurfaces validHits.add(checked) @@ -428,15 +425,9 @@ object BuildSimulator { val blockHit = checkedHit.hit.blockResult ?: return@let val breakContext = BreakContext( - eye, - blockHit, - RotationRequest( - lookAtBlock(blockHit.blockPos, interactionConfig = interact), - rotation - ), - state, - player.activeHand, - instantBreakable(state, pos) + eye, blockHit, RotationRequest( + lookAtBlock(blockHit.blockPos, config = interact), rotation + ), state, player.activeHand, instantBreakable(state, pos) ) /* player has a better tool for the job available */ @@ -449,7 +440,7 @@ object BuildSimulator { acc.add(BreakResult.Break(pos, breakContext)) return acc } ?: run { - acc.add(BuildResult.WrongItem(pos, breakContext, bestTool, player.activeItem)) + acc.add(BuildResult.WrongItem(pos, breakContext, bestTool, player.activeItem, inventory)) return acc } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index 26ddf85b4..0b0da6b13 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.material.container +import com.lambda.config.groups.InventoryConfig import com.lambda.core.Loadable import com.lambda.event.events.InventoryEvent import com.lambda.event.events.PlayerEvent @@ -91,8 +92,8 @@ object ContainerManager : Loadable { fun container() = container.flatMap { setOf(it) + it.shulkerContainer }.sorted() - fun StackSelection.transfer(destination: MaterialContainer) = - findContainerWithMaterial(this)?.transfer(this, destination) + fun StackSelection.transfer(destination: MaterialContainer, inventory: InventoryConfig = TaskFlowModule.inventory) = + findContainerWithMaterial(this, inventory)?.transfer(this, destination) fun findContainer( block: (MaterialContainer) -> Boolean, @@ -100,8 +101,9 @@ object ContainerManager : Loadable { fun findContainerWithMaterial( selection: StackSelection, + inventory: InventoryConfig ): MaterialContainer? = - containerWithMaterial(selection).firstOrNull() + containerWithMaterial(selection, inventory).firstOrNull() fun findContainerWithSpace( selection: StackSelection, @@ -110,33 +112,36 @@ object ContainerManager : Loadable { fun containerWithMaterial( selection: StackSelection, + inventory: InventoryConfig = TaskFlowModule.inventory, ): List = container() - .sortedWith(TaskFlowModule.inventory.providerPriority.materialComparator(selection)) + .sortedWith(inventory.providerPriority.materialComparator(selection)) .filter { it.materialAvailable(selection) >= selection.count } fun containerWithSpace( selection: StackSelection, + inventory: InventoryConfig = TaskFlowModule.inventory, ): List = container() - .sortedWith(TaskFlowModule.inventory.providerPriority.spaceComparator(selection)) + .sortedWith(inventory.providerPriority.spaceComparator(selection)) .filter { it.spaceAvailable(selection) >= selection.count } fun findBestAvailableTool( blockState: BlockState, availableTools: Set = ItemUtils.tools, + inventory: InventoryConfig = TaskFlowModule.inventory, ) = availableTools.map { it to it.getMiningSpeedMultiplier(it.defaultStack, blockState) }.filter { (item, speed) -> speed > 1.0 && item.isSuitableFor(blockState) - && containerWithMaterial(item.select()).isNotEmpty() + && containerWithMaterial(item.select(), inventory).isNotEmpty() }.maxByOrNull { it.second }?.first - fun findDisposable() = container().find { container -> - TaskFlowModule.inventory.disposables.any { container.materialAvailable(it.item.select()) >= 0 } + fun findDisposable(inventory: InventoryConfig = TaskFlowModule.inventory) = container().find { container -> + inventory.disposables.any { container.materialAvailable(it.item.select()) >= 0 } } class NoContainerFound(selection: StackSelection) : Exception("No container found matching $selection") diff --git a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt index 15ab9caf6..d09e6213b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt @@ -24,14 +24,14 @@ import java.util.concurrent.ConcurrentHashMap * It provides a mechanism to register requests and select the highest priority request * for processing. */ -abstract class RequestHandler { +abstract class RequestHandler { - private val requestMap = ConcurrentHashMap, T>() + private val requestMap = ConcurrentHashMap, R>() /** * The currently active request. */ - var currentRequest: T? = null; protected set + var currentRequest: R? = null; protected set /** * Registers a new request with the given configuration. @@ -40,7 +40,7 @@ abstract class RequestHandler { * @param request The request to register. * @return The registered request. */ - fun registerRequest(config: RequestConfig, request: T): T { + fun registerRequest(config: RequestConfig, request: R): R { requestMap[config] = request return request } @@ -53,7 +53,7 @@ abstract class RequestHandler { */ protected fun updateRequest( keepIfNull: Boolean = false, - filter: (Map.Entry, T>) -> Boolean = { true } + filter: (Map.Entry, R>) -> Boolean = { true } ): Boolean { val prev = currentRequest diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt index 7c6be4db3..67d2bd924 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationConfig.kt @@ -19,7 +19,6 @@ package com.lambda.interaction.request.rotation import com.lambda.interaction.request.Priority import com.lambda.interaction.request.RequestConfig -import com.lambda.interaction.request.rotation.visibilty.RotationTarget /* * Abstract base class for configuring rotation behavior. @@ -28,9 +27,10 @@ import com.lambda.interaction.request.rotation.visibilty.RotationTarget */ abstract class RotationConfig(priority: Priority) : RequestConfig(priority) { /** - * - [RotationMode.SILENT] Spoofing server-side rotation. - * - [RotationMode.SYNC] Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). - * - [RotationMode.LOCK] Locks the camera client-side. + * - [RotationMode.Silent] Spoofing server-side rotation. + * - [RotationMode.Sync] Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). + * - [RotationMode.Lock] Locks the camera client-side. + * - [RotationMode.None] No rotation. */ abstract val rotationMode: RotationMode @@ -49,7 +49,7 @@ abstract class RotationConfig(priority: Priority) : RequestConfig(), Loadable { override fun load() = "Loaded Rotation Manager" /** - * Registers a listener that is called right before [RotationManager] updates the context and the rotation + * Registers a listener called right before [RotationManager] updates the context and the rotation * * Use this if you need to match specific state of the client * (when the player and its input are already updated this tick and similar) / @@ -99,7 +99,7 @@ object RotationManager : RequestHandler(), Loadable { currentRotation = targetRotation.fixSensitivity(prevRotation) // Handle LOCK mode - if (currentRequest?.mode == RotationMode.LOCK) { + if (currentRequest?.mode == RotationMode.Lock) { player.yaw = currentRotation.yawF player.pitch = currentRotation.pitchF } @@ -139,7 +139,7 @@ object RotationManager : RequestHandler(), Loadable { @JvmStatic val lockRotation get() = - if (currentRequest?.mode == RotationMode.LOCK) smoothRotation else null + if (currentRequest?.mode == RotationMode.Lock) smoothRotation else null @JvmStatic val renderYaw @@ -154,30 +154,30 @@ object RotationManager : RequestHandler(), Loadable { @JvmStatic val handYaw get() = - if (currentRequest?.mode == RotationMode.LOCK) currentRotation.yaw.toFloat() else null + if (currentRequest?.mode == RotationMode.Lock) currentRotation.yaw.toFloat() else null @JvmStatic val handPitch get() = - if (currentRequest?.mode == RotationMode.LOCK) currentRotation.pitch.toFloat() else null + if (currentRequest?.mode == RotationMode.Lock) currentRotation.pitch.toFloat() else null @JvmStatic val movementYaw: Float? get() { - if (currentRequest?.mode == RotationMode.SILENT) return null + if (currentRequest?.mode == RotationMode.Silent) return null return currentRotation.yaw.toFloat() } @JvmStatic val movementPitch: Float? get() { - if (currentRequest?.mode == RotationMode.SILENT) return null + if (currentRequest?.mode == RotationMode.Silent) return null return currentRotation.pitch.toFloat() } @JvmStatic fun getRotationForVector(deltaTime: Double): Vec2d? { - if (currentRequest?.mode == RotationMode.SILENT) return null + if (currentRequest?.mode == RotationMode.Silent) return null val rot = lerp(deltaTime, prevRotation, currentRotation) return Vec2d(rot.yaw, rot.pitch) @@ -186,8 +186,8 @@ object RotationManager : RequestHandler(), Loadable { object BaritoneProcessor { private var baritoneContext: RotationRequest? = null private val baritoneRotation get() = Baritone.rotation.apply { - if (rotationMode == RotationMode.SYNC) return@apply - rotationMode = RotationMode.SYNC + if (rotationMode == RotationMode.Sync) return@apply + rotationMode = RotationMode.Sync } private val movementYawList = arrayOf( @@ -225,7 +225,7 @@ object RotationManager : RequestHandler(), Loadable { // Actual yaw used by the physics engine var actualYaw = currentRotation.yaw - if (currentRequest?.mode == RotationMode.SILENT) { + if (currentRequest?.mode == RotationMode.Silent) { actualYaw = player.yaw.toDouble() } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt index 643fbeb13..2eafbd919 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationMode.kt @@ -18,14 +18,14 @@ package com.lambda.interaction.request.rotation /** - * @property SILENT Spoofing server-side rotation. - * @property SYNC Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). - * @property LOCK Locks the camera client-side. - * @property NONE No rotation. + * @property Silent Spoofing server-side rotation. + * @property Sync Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). + * @property Lock Locks the camera client-side. + * @property None No rotation. */ enum class RotationMode { - SILENT, - SYNC, - LOCK, - NONE + Silent, + Sync, + Lock, + None } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index ed15d96ff..2b839a6f8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -32,16 +32,6 @@ data class RotationRequest( val speedMultiplier: Double = 1.0 ) : Request(prio) { - constructor( - target: RotationTarget, - priority: Priority, - mode: RotationMode, - keepTicks: Int = 3, - decayTicks: Int = 0, - turnSpeed: Double = 180.0, - speedMultiplier: Double = 1.0 - ) : this(target, priority, mode, keepTicks, decayTicks, { turnSpeed }, speedMultiplier) - constructor( target: RotationTarget, config: RotationConfig, @@ -49,7 +39,7 @@ data class RotationRequest( ) : this(target, config.priority, config.rotationMode, config.keepTicks, config.decayTicks, config::turnSpeed, speedMultiplier) override val done: Boolean get() = - mode == RotationMode.NONE || runSafe { + mode == RotationMode.None || runSafe { target.verify(target) } == true } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index baf12bf97..b4ffc3793 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -22,7 +22,7 @@ import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe -import com.lambda.util.collections.cached +import com.lambda.util.collections.resettableLazy /** * Represents a target for rotation. @@ -37,7 +37,7 @@ data class RotationTarget( private val buildRotation: SafeContext.() -> Rotation?, ) { - val targetRotation = cached { + val targetRotation = resettableLazy { runSafe { buildRotation() } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index 0a8b28a70..f195acbf4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -72,11 +72,11 @@ fun lookAtHit(hit: RequestedHit, rotation: SafeContext.() -> Rotation?) = @RotationDsl fun lookAtHit( hit: HitResult, - interactionConfig: InteractionConfig = TaskFlowModule.interact, + config: InteractionConfig = TaskFlowModule.interact, ): RotationTarget? { return when (hit) { - is BlockHitResult -> lookAtBlock(hit.blockPos, setOf(hit.side), interactionConfig) - is EntityHitResult -> lookAtEntity(hit.entity as? LivingEntity ?: return null, interactionConfig) + is BlockHitResult -> lookAtBlock(hit.blockPos, setOf(hit.side), config) + is EntityHitResult -> lookAtEntity(hit.entity as? LivingEntity ?: return null, config) else -> null } } @@ -85,24 +85,24 @@ fun lookAtHit( * Creates a [RotationTarget] based on an entity. * * @param entity The target entity. - * @param interactionConfig The interaction configuration. Defaults to [TaskFlowModule.interact]. + * @param config The interaction configuration. Defaults to [TaskFlowModule.interact]. * @return A [RotationTarget] instance. */ @RotationDsl fun lookAtEntity( entity: LivingEntity, - interactionConfig: InteractionConfig = TaskFlowModule.interact + config: InteractionConfig = TaskFlowModule.interact ): RotationTarget { - val requestedHit = entityHit(entity, interactionConfig.attackReach) + val requestedHit = entityHit(entity, config.attackReach) return RotationTarget(requestedHit) { findRotation( requestedHit.getBoundingBoxes(), - interactionConfig.attackReach, + config.attackReach, player.eyePos, ALL_SIDES, InteractionMask.ENTITY, - interactionConfig + config ) { requestedHit.verifyHit(hit) }?.targetRotation } } @@ -112,25 +112,25 @@ fun lookAtEntity( * * @param pos The position of the block. * @param sides The set of sides to consider for the hit. Defaults to [ALL_SIDES]. - * @param interactionConfig The interaction configuration. Defaults to [TaskFlowModule.interact]. + * @param config The interaction configuration. Defaults to [TaskFlowModule.interact]. * @return A [RotationTarget] instance. */ @RotationDsl fun lookAtBlock( pos: BlockPos, sides: Set = ALL_SIDES, - interactionConfig: InteractionConfig = TaskFlowModule.interact, + config: InteractionConfig = TaskFlowModule.interact, ): RotationTarget { - val requestedHit = blockHit(pos, sides, interactionConfig.placeReach) + val requestedHit = blockHit(pos, sides, config.interactReach) return RotationTarget(requestedHit) { findRotation( requestedHit.getBoundingBoxes(), - interactionConfig.placeReach, + config.interactReach, player.eyePos, sides, InteractionMask.BLOCK, - interactionConfig + config ) { requestedHit.verifyHit(hit) }?.targetRotation } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt b/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt index 1e741fea7..b8c41ed20 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt @@ -19,7 +19,7 @@ package com.lambda.module.hud import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag -import com.lambda.task.TaskFlow +import com.lambda.task.RootTask import com.lambda.util.math.Vec2d object TaskFlowHUD : HudModule( @@ -31,7 +31,7 @@ object TaskFlowHUD : HudModule( init { onRender { - TaskFlow.toString().lines().forEachIndexed { index, line -> + RootTask.toString().lines().forEachIndexed { index, line -> font.build(line, Vec2d(position.x, position.y + index * (font.getHeight(font.scaleMultiplier) + 2.0))) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt index 719724949..a6208e029 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt @@ -22,7 +22,7 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.TaskFlow.run +import com.lambda.task.RootTask.run import com.lambda.task.tasks.AcquireMaterial.Companion.acquire import net.minecraft.item.Items diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 6a90d0e20..139ef7be4 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -69,7 +69,7 @@ object Speed : Module( private val ncpTimerBoost by setting("Timer Boost", 1.08, 1.0..1.1, 0.01) { mode == Mode.NCP_STRAFE } // Grim - private val rotationConfig = RotationConfig.Instant(RotationMode.SYNC, Int.MIN_VALUE + 1) + private val rotationConfig = RotationConfig.Instant(RotationMode.Sync, Int.MIN_VALUE + 1) private var prevTickJumping = false diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt index 6d464dc82..53bbd0c5b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt @@ -22,10 +22,8 @@ import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.event.events.* import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.rotation.Rotation -import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.request.rotation.RotationMode -import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.interaction.request.rotation.visibilty.lookAtHit import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -44,7 +42,6 @@ import com.lambda.util.player.MovementUtils.roundedForward import com.lambda.util.player.MovementUtils.roundedStrafing import com.lambda.util.player.MovementUtils.verticalMovement import com.lambda.util.world.raycast.RayCastUtils.orMiss -import com.lambda.util.world.raycast.RayCastUtils.orNull import net.minecraft.client.option.Perspective import net.minecraft.util.math.Vec3d @@ -58,7 +55,7 @@ object Freecam : Module( private val reach by setting("Reach", 10.0, 1.0..100.0, 1.0, "Freecam reach distance") private val rotateToTarget by setting("Rotate to target", true) - private val rotationConfig = RotationConfig.Instant(RotationMode.LOCK) + private val rotationConfig = RotationConfig.Instant(RotationMode.Lock) private var lastPerspective = Perspective.FIRST_PERSON private var prevPosition: Vec3d = Vec3d.ZERO diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 7a69b28eb..07adb6cac 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -17,12 +17,16 @@ package com.lambda.module.modules.player +import com.lambda.config.groups.BuildSettings +import com.lambda.config.groups.InteractionSettings +import com.lambda.config.groups.InventorySettings +import com.lambda.config.groups.RotationSettings import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.Task -import com.lambda.task.TaskFlow.run +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.info @@ -32,6 +36,7 @@ import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.rotateClockwise import com.lambda.util.player.MovementUtils.octant import com.lambda.util.world.StructureUtils.generateDirectionalTube +import com.lambda.util.world.raycast.InteractionMask import net.minecraft.block.Blocks import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -43,20 +48,27 @@ object HighwayTools : Module( description = "Auto highway builder", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) ) { - private val height by setting("Height", 4, 2..10, 1) - private val width by setting("Width", 6, 1..30, 1) - private val pavement by setting("Pavement", Material.Block, description = "Material for the pavement") - private val rimHeight by setting("Pavement Rim Height", 1, 0..6, 1) { pavement != Material.None } - private val cornerBlock by setting("Corner", Corner.None, description = "Include corner blocks in the highway") { pavement != Material.None } - private val pavementMaterial by setting("Pavement Material", Blocks.OBSIDIAN, description = "Material to build the highway with") { pavement == Material.Block } - private val floor by setting("Floor", Material.None, description = "Material for the floor") - private val floorMaterial by setting("Floor Material", Blocks.NETHERRACK, description = "Material to build the floor with") { floor == Material.Block } - private val walls by setting("Walls", Material.None, description = "Material for the walls") - private val wallMaterial by setting("Wall Material", Blocks.NETHERRACK, description = "Material to build the walls with") { walls == Material.Block } - private val ceiling by setting("Ceiling", Material.None, description = "Material for the ceiling") - private val ceilingMaterial by setting("Ceiling Material", Blocks.OBSIDIAN, description = "Material to build the ceiling with") { ceiling == Material.Block } - private val distance by setting("Distance", -1, -1..1000000, 1, description = "Distance to build the highway/tunnel (negative for infinite)") - private val sliceSize by setting("Slice Size", 3, 1..5, 1, description = "Number of slices to build at once") + private val page by setting("Page", Page.Structure) + + private val height by setting("Height", 4, 2..10, 1) { page == Page.Structure } + private val width by setting("Width", 6, 1..30, 1) { page == Page.Structure } + private val pavement by setting("Pavement", Material.Block, description = "Material for the pavement") { page == Page.Structure } + private val rimHeight by setting("Pavement Rim Height", 1, 0..6, 1) { page == Page.Structure && pavement != Material.None } + private val cornerBlock by setting("Corner", Corner.None, description = "Include corner blocks in the highway") { page == Page.Structure && pavement != Material.None } + private val pavementMaterial by setting("Pavement Material", Blocks.OBSIDIAN, description = "Material to build the highway with") { page == Page.Structure && pavement == Material.Block } + private val floor by setting("Floor", Material.None, description = "Material for the floor") { page == Page.Structure } + private val floorMaterial by setting("Floor Material", Blocks.NETHERRACK, description = "Material to build the floor with") { page == Page.Structure && floor == Material.Block } + private val walls by setting("Walls", Material.None, description = "Material for the walls") { page == Page.Structure } + private val wallMaterial by setting("Wall Material", Blocks.NETHERRACK, description = "Material to build the walls with") { page == Page.Structure && walls == Material.Block } + private val ceiling by setting("Ceiling", Material.None, description = "Material for the ceiling") { page == Page.Structure } + private val ceilingMaterial by setting("Ceiling Material", Blocks.OBSIDIAN, description = "Material to build the ceiling with") { page == Page.Structure && ceiling == Material.Block } + private val distance by setting("Distance", -1, -1..1000000, 1, description = "Distance to build the highway/tunnel (negative for infinite)") { page == Page.Structure } + private val sliceSize by setting("Slice Size", 3, 1..5, 1, description = "Number of slices to build at once") { page == Page.Structure } + + val build = BuildSettings(this) { page == Page.Build } + val rotation = RotationSettings(this) { page == Page.Rotation } + val interact = InteractionSettings(this, InteractionMask.BOTH) { page == Page.Interaction } + val inventory = InventorySettings(this) { page == Page.Inventory } private var octant = EightWayDirection.NORTH private var distanceMoved = 0 @@ -65,14 +77,15 @@ object HighwayTools : Module( private var runningTask: Task<*>? = null enum class Material { - None, - Solid, - Block, + None, Solid, Block, } enum class Corner { - None, - Solid, + None, Solid, + } + + enum class Page { + Structure, Build, Rotation, Interaction, Inventory } init { @@ -101,7 +114,12 @@ object HighwayTools : Module( structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) } - runningTask = structure.toBlueprint().build().finally { + runningTask = structure.toBlueprint().build( + build = build, + rotation = rotation, + interact = interact, + inventory = inventory, + ).finally { if (distanceMoved < distance || distance < 0) { buildSlice() } else { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt index 50ec1bd43..173879f32 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt @@ -24,7 +24,7 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.Task -import com.lambda.task.TaskFlow.run +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock import com.lambda.task.tasks.OpenContainer import com.lambda.task.tasks.PlaceContainer diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 606d3618f..3d2e90bd0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -23,7 +23,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.Task -import com.lambda.task.TaskFlow.run +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index 221579212..5007831fe 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -25,11 +25,9 @@ import com.lambda.core.TimerManager import com.lambda.event.EventFlow.lambdaScope import com.lambda.event.events.KeyboardEvent import com.lambda.event.events.MovementEvent -import com.lambda.event.events.RotationEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.RotationManager.onRotate -import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.interaction.request.rotation.RotationMode import com.lambda.interaction.request.rotation.visibilty.lookAt import com.lambda.module.Module @@ -81,8 +79,8 @@ object Replay : Module( private val deviationThreshold by setting("Deviation threshold", 0.1, 0.1..5.0, 0.1, description = "The threshold for the deviation to cancel the replay.") { cancelOnDeviation } private val lockCamera by setting("Lock Camera", true) - private val rotationConfig = object : RotationConfig.Instant(RotationMode.SYNC) { - override val rotationMode = if (lockCamera) RotationMode.LOCK else RotationMode.SYNC + private val rotationConfig = object : RotationConfig.Instant(RotationMode.Sync) { + override val rotationMode = if (lockCamera) RotationMode.Lock else RotationMode.Sync } enum class State { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index 2705e5bc1..fd993d3c6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -134,7 +134,7 @@ object Scaffold : Module( val info = updatePlaceInfo() ?: return@onRotate lastRequest = lookAtHit( - blockHit(info.clickPos, info.clickSide, interactionConfig.placeReach) + blockHit(info.clickPos, info.clickSide, interactionConfig.interactReach) ) { rotate(info) }.requestBy(rotationConfig) } @@ -178,7 +178,7 @@ object Scaffold : Module( // Getting the latest block of the placement sequence placeInfo = buildPlaceInfo( basePos = BlockPos(player.pos.x.floorToInt(), y, player.pos.z.floorToInt()), - range = interactionConfig.placeReach + 2, + range = interactionConfig.interactReach + 2, sides = builderSideMask ) @@ -201,7 +201,7 @@ object Scaffold : Module( private fun SafeContext.rotate(info: PlaceInfo): Rotation? { val eye = player.eyePos - val reach = interactionConfig.placeReach + val reach = interactionConfig.interactReach val reachSq = reach.pow(2) val input = newMovementInput() @@ -373,7 +373,7 @@ object Scaffold : Module( // Checks if the rotation matches the placement requirements private fun castRotation(rotation: Rotation, info: PlaceInfo): BlockHitResult? { - val blockResult = rotation.rayCast(interactionConfig.placeReach)?.blockResult ?: return null + val blockResult = rotation.rayCast(interactionConfig.interactReach)?.blockResult ?: return null if (blockResult.blockPos != info.clickPos || blockResult.side != info.clickSide) return null return blockResult } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt b/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt index 2384413b3..c3303369a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt @@ -26,7 +26,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.Task -import com.lambda.task.TaskFlow.run +import com.lambda.task.RootTask.run import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockBox diff --git a/common/src/main/kotlin/com/lambda/task/TaskFlow.kt b/common/src/main/kotlin/com/lambda/task/RootTask.kt similarity index 90% rename from common/src/main/kotlin/com/lambda/task/TaskFlow.kt rename to common/src/main/kotlin/com/lambda/task/RootTask.kt index 30690075d..9899a8e65 100644 --- a/common/src/main/kotlin/com/lambda/task/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/task/RootTask.kt @@ -19,12 +19,12 @@ package com.lambda.task import com.lambda.threading.runSafe -object TaskFlow : Task() { - override val name get() = "TaskFlow" +object RootTask : Task() { + override val name get() = "Root Task" @Ta5kBuilder inline fun > T.run(): T { - execute(this@TaskFlow) + execute(this@RootTask) return this } diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 557b247df..3c45fdcbe 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -115,7 +115,7 @@ abstract class Task : Nameable, Muteable { LOG.info("${owner.name} started $name") if (!unpausable || pauseParent) { LOG.info("$name deactivating parent ${owner.name}") - if (owner !is TaskFlow) owner.deactivate() + if (owner !is RootTask) owner.deactivate() } state = State.RUNNING runSafe { runCatching { onStart() }.onFailure { failure(it) } } @@ -168,7 +168,7 @@ abstract class Task : Nameable, Muteable { @Ta5kBuilder fun cancel() { cancelSubTasks() - if (this is TaskFlow) return + if (this is RootTask) return if (state == State.COMPLETED || state == State.CANCELLED) return state = State.CANCELLED unsubscribe() @@ -310,10 +310,10 @@ abstract class Task : Nameable, Muteable { buildString { appendTaskTree(this@Task) } private fun StringBuilder.appendTaskTree(task: Task<*>, level: Int = 0) { - appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is TaskFlow) " [${task.state.display}]" else "") + appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is RootTask) " [${task.state.display}]" else "") if (!TaskFlowModule.showAllEntries && (task.state == State.COMPLETED || task.state == State.CANCELLED)) return task.subTasks.forEach { - if (!TaskFlowModule.showAllEntries && task is TaskFlow && (it.state == State.COMPLETED || it.state == State.CANCELLED)) return@forEach + if (!TaskFlowModule.showAllEntries && task is RootTask && (it.state == State.COMPLETED || it.state == State.CANCELLED)) return@forEach appendTaskTree(it, level + 1) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index b5af8ceb7..24852f94e 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -17,20 +17,23 @@ package com.lambda.task.tasks +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial +import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task class AcquireMaterial @Ta5kBuilder constructor( val selection: StackSelection, + val inventory: InventoryConfig ) : Task() { override val name: String get() = "Acquiring $selection" override fun SafeContext.onStart() { - findContainerWithMaterial(selection) + findContainerWithMaterial(selection, inventory) ?.withdraw(selection) ?.finally { success(selection) @@ -41,6 +44,6 @@ class AcquireMaterial @Ta5kBuilder constructor( companion object { @Ta5kBuilder fun acquire(selection: () -> StackSelection) = - AcquireMaterial(selection()) + AcquireMaterial(selection(), TaskFlowModule.inventory) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index d9d1b34b5..65e0edc89 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -20,6 +20,7 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.event.events.MovementEvent @@ -57,6 +58,7 @@ class BuildTask @Ta5kBuilder constructor( private val build: BuildConfig = TaskFlowModule.build, private val rotation: RotationConfig = TaskFlowModule.rotation, private val interact: InteractionConfig = TaskFlowModule.interact, + private val inventory: InventoryConfig = TaskFlowModule.inventory, ) : Task() { override val name: String get() = "Building $blueprint with ${(placements / (age / 20.0 + 0.001)).string} p/s" @@ -82,10 +84,10 @@ class BuildTask @Ta5kBuilder constructor( } currentPlacement?.let { context -> + currentPlacement = null if (build.rotateForPlace && !context.rotation.done) return@listen context.place(interact.swingHand) pendingPlacements.add(context) - currentPlacement = null } (blueprint as? DynamicBlueprint)?.update() @@ -96,7 +98,7 @@ class BuildTask @Ta5kBuilder constructor( } // ToDo: Simulate for each pair player positions that work - val results = blueprint.simulate(player.getCameraPosVec(mc.tickDelta)) + val results = blueprint.simulate(player.getCameraPosVec(mc.tickDelta), interact, rotation, inventory) TaskFlowModule.drawables = results.filterIsInstance().plus(pendingPlacements.toList()) val instantResults = results.filterIsInstance() @@ -137,11 +139,6 @@ class BuildTask @Ta5kBuilder constructor( is PlaceResult.Place -> { if (pendingPlacements.size >= build.maxPendingPlacements) return@listen -// if (!result.context.rotation.isValid) { -// currentPlacement = result.context -// return@listen -// } - currentPlacement = result.context } @@ -203,7 +200,8 @@ class BuildTask @Ta5kBuilder constructor( build: BuildConfig = TaskFlowModule.build, rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interact, - ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact) + inventory: InventoryConfig = TaskFlowModule.inventory, + ) = BuildTask(toBlueprint(), finishOnDone, collectDrops, build, rotation, interact, inventory) @Ta5kBuilder fun Blueprint.build( @@ -212,7 +210,8 @@ class BuildTask @Ta5kBuilder constructor( build: BuildConfig = TaskFlowModule.build, rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interact, - ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact) + inventory: InventoryConfig = TaskFlowModule.inventory, + ) = BuildTask(this, finishOnDone, collectDrops, build, rotation, interact, inventory) @Ta5kBuilder fun breakAndCollectBlock( @@ -222,9 +221,10 @@ class BuildTask @Ta5kBuilder constructor( build: BuildConfig = TaskFlowModule.build, rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interact, + inventory: InventoryConfig = TaskFlowModule.inventory, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact + finishOnDone, collectDrops, build, rotation, interact, inventory ) @Ta5kBuilder @@ -235,9 +235,10 @@ class BuildTask @Ta5kBuilder constructor( build: BuildConfig = TaskFlowModule.build, rotation: RotationConfig = TaskFlowModule.rotation, interact: InteractionConfig = TaskFlowModule.interact, + inventory: InventoryConfig = TaskFlowModule.inventory, ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, build, rotation, interact + finishOnDone, collectDrops, build, rotation, interact, inventory ) } } diff --git a/common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt b/common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt deleted file mode 100644 index 9359b099c..000000000 --- a/common/src/main/kotlin/com/lambda/util/collections/ResettableLazy.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.util.collections - -class ResettableLazy(private val initializer: () -> T) { - private var _value: T? = null - - val value: T? - get() { - if (_value == null) _value = initializer() - return _value - } - - fun reset() { - _value = null - } -} - -fun resettableLazy(initializer: () -> T) = ResettableLazy(initializer) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/collections/CachedValue.kt b/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt similarity index 87% rename from common/src/main/kotlin/com/lambda/util/collections/CachedValue.kt rename to common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt index 870fd7664..398a5ff24 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/CachedValue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt @@ -17,7 +17,7 @@ package com.lambda.util.collections -class CachedValue (private val initializer: () -> T) { +class UpdatableLazy(private val initializer: () -> T) { private var _value: T? = null val value: T? @@ -31,4 +31,4 @@ class CachedValue (private val initializer: () -> T) { } } -fun cached(initializer: () -> T) = CachedValue(initializer) \ No newline at end of file +fun resettableLazy(initializer: () -> T) = UpdatableLazy(initializer) \ No newline at end of file From d22b73df576a6865d3044f87c491bce5b7e705f4 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 02:53:56 +0100 Subject: [PATCH 48/69] Fix default values and visibility --- .../com/lambda/config/groups/InteractionSettings.kt | 4 ++-- .../com/lambda/module/modules/player/HighwayTools.kt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index 0ba046c84..0c831ef5b 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -57,8 +57,8 @@ class InteractionSettings( // Point scan override val strictRayCast by c.setting("Strict Raycast", true, "Whether to include the environment to the ray cast context", vis) override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible", vis) - override val resolution by c.setting("Resolution", 8, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", vis) - override val pointSelection by c.setting("Point Selection", PointSelection.ByRotation, "The way to select the best point", vis) + override val resolution by c.setting("Resolution", 5, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", vis) + override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The way to select the best point", vis) // Swing override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", vis) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 07adb6cac..eab5f1f07 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -65,10 +65,10 @@ object HighwayTools : Module( private val distance by setting("Distance", -1, -1..1000000, 1, description = "Distance to build the highway/tunnel (negative for infinite)") { page == Page.Structure } private val sliceSize by setting("Slice Size", 3, 1..5, 1, description = "Number of slices to build at once") { page == Page.Structure } - val build = BuildSettings(this) { page == Page.Build } - val rotation = RotationSettings(this) { page == Page.Rotation } - val interact = InteractionSettings(this, InteractionMask.BOTH) { page == Page.Interaction } - val inventory = InventorySettings(this) { page == Page.Inventory } + private val build = BuildSettings(this) { page == Page.Build } + private val rotation = RotationSettings(this) { page == Page.Rotation } + private val interact = InteractionSettings(this, InteractionMask.BOTH) { page == Page.Interaction } + private val inventory = InventorySettings(this) { page == Page.Inventory } private var octant = EightWayDirection.NORTH private var distanceMoved = 0 From 387652338cd9368102180c1dd1f4c226a1cdd5cb Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 02:55:53 +0100 Subject: [PATCH 49/69] UpdatableLazy Kdocs --- .../lambda/util/collections/UpdatableLazy.kt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt b/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt index 398a5ff24..f5a18a074 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt @@ -17,18 +17,55 @@ package com.lambda.util.collections +/** + * A lazy-initialized value holder that allows the stored value to be reset and re-initialized on demand. + * + * This class supports lazy initialization for a value of type [T] using a provided initializer function. + * Once the value is accessed, it is initialized and stored. The `update` function can be called to reset + * the value, allowing it to be re-initialized using the same initializer function. + * + * @param T The type of the value being lazily initialized and managed. + * @constructor Accepts an initializer function that defines how the value should be created. + */ class UpdatableLazy(private val initializer: () -> T) { private var _value: T? = null + /** + * Lazily initializes and retrieves a value of type [T] using the provided initializer function. + * If the value has not been initialized previously, the initializer function is called + * to generate the value, which is then cached for subsequent accesses. + * + * This property ensures that the value is only initialized when it is first accessed, + * and maintains its state until explicitly updated or reset. + * + * @return The lazily initialized value, or `null` if the initializer function + * is designed to produce a `null` result or has not been called yet. + */ val value: T? get() { if (_value == null) _value = initializer() return _value } + /** + * Resets the current value to a new value generated by the initializer function. + * + * This function effectively re-initializes the stored value by discarding the existing value, + * if any, and calling the initializer function to compute a new value. + * It is useful for scenarios where the lazily computed value needs to be refreshed or updated + * explicitly. + */ fun update() { _value = initializer() } } +/** + * Provides a lazily-initialized value that can be reset and re-initialized on demand. + * This function utilizes an `UpdatableLazy` to manage the lazy initialization and allow updates. + * + * @param T The type of the value to be lazily initialized. + * @param initializer A lambda function that defines how the value should be computed. + * @return An `UpdatableLazy` instance capable of managing a lazily-initialized value. + */ fun resettableLazy(initializer: () -> T) = UpdatableLazy(initializer) \ No newline at end of file From e60615f5ce04fd50ac2103fc5045bd4e67194d8a Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 03:25:09 +0100 Subject: [PATCH 50/69] Some fixes --- .../construction/simulation/Simulation.kt | 23 ++++++++++++---- .../request/rotation/RotationManager.kt | 8 +----- .../lambda/module/modules/client/Baritone.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 26 ++++++++++++++++--- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index b6e7dcfdd..78990bc39 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -17,10 +17,13 @@ package com.lambda.interaction.construction.simulation +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.simulation.BuildSimulator.simulate +import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState @@ -32,11 +35,18 @@ import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d -data class Simulation(val blueprint: Blueprint) { +data class Simulation( + val blueprint: Blueprint, + val interact: InteractionConfig = TaskFlowModule.interact, + val rotation: RotationConfig = TaskFlowModule.rotation, + val inventory: InventoryConfig = TaskFlowModule.inventory, +) { private val cache: MutableMap> = mutableMapOf() private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, ClientPlayerEntity.DEFAULT_EYE_HEIGHT.toDouble(), 0.5) - fun simulate(pos: FastVector) = + fun simulate( + pos: FastVector + ) = cache.getOrPut(pos) { val view = pos.toView() runSafe { @@ -50,8 +60,7 @@ data class Simulation(val blueprint: Blueprint) { ) return@getOrPut emptySet() } - // ToDo: wtf was that "reach = TaskFlowModule.interact.reach - 1" - blueprint.simulate(view) + blueprint.simulate(view, interact, rotation, inventory) } private fun SafeContext.playerFitsIn(pos: Vec3d): Boolean { @@ -61,6 +70,10 @@ data class Simulation(val blueprint: Blueprint) { } companion object { - fun Blueprint.simulation() = Simulation(this) + fun Blueprint.simulation( + interact: InteractionConfig = TaskFlowModule.interact, + rotation: RotationConfig = TaskFlowModule.rotation, + inventory: InventoryConfig = TaskFlowModule.inventory, + ) = Simulation(this, interact, rotation, inventory) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 7b0e3d7c7..95f735103 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -185,10 +185,6 @@ object RotationManager : RequestHandler(), Loadable { object BaritoneProcessor { private var baritoneContext: RotationRequest? = null - private val baritoneRotation get() = Baritone.rotation.apply { - if (rotationMode == RotationMode.Sync) return@apply - rotationMode = RotationMode.Sync - } private val movementYawList = arrayOf( 0.0, 45.0, @@ -199,9 +195,7 @@ object RotationManager : RequestHandler(), Loadable { @JvmStatic fun handleBaritoneRotation(yaw: Float, pitch: Float) { - lookAt( - Rotation(yaw, pitch) - ).requestBy(baritoneRotation) + lookAt(Rotation(yaw, pitch)).requestBy(Baritone.rotation) } @JvmStatic diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt index fc79bf034..8ac615b68 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt @@ -26,5 +26,5 @@ object Baritone : Module( description = "Baritone configuration", defaultTags = setOf(ModuleTag.CLIENT) ) { - val rotation = RotationSettings(this, Int.MIN_VALUE) + val rotation = RotationSettings(this, Int.MAX_VALUE) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 65e0edc89..35a1a4e65 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -20,6 +20,7 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext @@ -39,6 +40,7 @@ import com.lambda.interaction.construction.simulation.BuildGoal import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.rotation.visibilty.PointSelection import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -123,13 +125,29 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.Ignored, is BuildResult.Unbreakable, is BuildResult.Restricted, - is BuildResult.NoPermission, - -> { + is BuildResult.NoPermission -> { if (finishOnDone) success() } - is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { - if (build.pathing) BaritoneUtils.setGoalAndPath(BuildGoal(blueprint.simulation())) + is BuildResult.NotVisible, + is PlaceResult.NoIntegrity -> { + if (!build.pathing) return@listen + // ToDo: + // Solve the problem that baritone stops pathing when it thinks it is in a valid goal + // but the player position does not perfectly match the simulated position + // hacky fix for now is to walk "closer" but it wont work in every situation + val interaction = object : InteractionConfig { + override val attackReach = interact.attackReach + override val interactReach = interact.interactReach - 1 + override val scanReach = interact.scanReach + override val strictRayCast = interact.strictRayCast + override val checkSideVisibility = interact.checkSideVisibility + override val resolution = interact.resolution + override val pointSelection = interact.pointSelection + override val swingHand = interact.swingHand + } + val goal = BuildGoal(blueprint.simulation(interaction, rotation, inventory)) + BaritoneUtils.setGoalAndPath(goal) } is Navigable -> { From fca9ae2490a901fdcf71fbe567ac22024922fa97 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 17:03:13 +0100 Subject: [PATCH 51/69] Decision making on tick post --- .../request/rotation/RotationManager.kt | 10 ++++--- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 27 +++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 95f735103..37d8f5b7c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -53,11 +53,13 @@ object RotationManager : RequestHandler(), Loadable { override fun load() = "Loaded Rotation Manager" /** - * Registers a listener called right before [RotationManager] updates the context and the rotation + * Registers a listener called immediately before [RotationManager] handles its context and applies rotation updates. * - * Use this if you need to match specific state of the client - * (when the player and its input are already updated this tick and similar) / - * when you don't know when to rotate / you simply want to request before rotation update + * This is useful if you need to synchronize with the latest player state (including inputs and movement), + * or if you must place your rotation requests as the tick is completed and just before the new rotation is processed. + * + * @param alwaysListen Whether to keep this listener active at all times. Defaults to false. + * @param block A callback providing a [SafeContext] where you can place rotation-related operations or other logic. */ fun Any.onRotate( alwaysListen: Boolean = false, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 35a1a4e65..dc20e9ff1 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -20,7 +20,6 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig -import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext @@ -40,7 +39,6 @@ import com.lambda.interaction.construction.simulation.BuildGoal import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.simulation.Simulation.Companion.simulation import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.rotation.visibilty.PointSelection import com.lambda.module.modules.client.TaskFlowModule import com.lambda.task.Task import com.lambda.util.BaritoneUtils @@ -86,12 +84,13 @@ class BuildTask @Ta5kBuilder constructor( } currentPlacement?.let { context -> - currentPlacement = null if (build.rotateForPlace && !context.rotation.done) return@listen context.place(interact.swingHand) pendingPlacements.add(context) } + } + listen { (blueprint as? DynamicBlueprint)?.update() if (finishOnDone && blueprint.structure.isEmpty()) { @@ -100,7 +99,7 @@ class BuildTask @Ta5kBuilder constructor( } // ToDo: Simulate for each pair player positions that work - val results = blueprint.simulate(player.getCameraPosVec(mc.tickDelta), interact, rotation, inventory) + val results = blueprint.simulate(player.eyePos, interact, rotation, inventory) TaskFlowModule.drawables = results.filterIsInstance().plus(pendingPlacements.toList()) val instantResults = results.filterIsInstance() @@ -115,12 +114,12 @@ class BuildTask @Ta5kBuilder constructor( return@listen } - val resultsWithoutPending = results.filterNot { res -> + val resultsWithoutPending = results.filterNot { result -> val blockedPositions = pendingPlacements.map { it.expectedPos } - res is PlaceResult.Place && res.context.expectedPos in blockedPositions + result is PlaceResult.Place && result.context.expectedPos in blockedPositions } - val result = resultsWithoutPending.minOrNull() ?: return@listen - when (result) { + val bestResult = resultsWithoutPending.minOrNull() ?: return@listen + when (bestResult) { is BuildResult.Done, is BuildResult.Ignored, is BuildResult.Unbreakable, @@ -151,23 +150,23 @@ class BuildTask @Ta5kBuilder constructor( } is Navigable -> { - if (build.pathing) BaritoneUtils.setGoalAndPath(result.goal) + if (build.pathing) BaritoneUtils.setGoalAndPath(bestResult.goal) } is PlaceResult.Place -> { if (pendingPlacements.size >= build.maxPendingPlacements) return@listen - currentPlacement = result.context + currentPlacement = bestResult.context } is Resolvable -> { - LOG.info("Resolving: ${result.name}") + LOG.info("Resolving: ${bestResult.name}") - if (result is BreakResult.Break) { - result.collectDrop = collectDrops + if (bestResult is BreakResult.Break) { + bestResult.collectDrop = collectDrops } - result.resolve().execute(this@BuildTask, pauseParent = result.pausesParent) + bestResult.resolve().execute(this@BuildTask, pauseParent = bestResult.pausesParent) } } } From f70179f20d83442a6c3407a5296ff4338b9b499c Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 17:37:31 +0100 Subject: [PATCH 52/69] Add build config to simulator --- .../construction/simulation/BuildSimulator.kt | 14 +++++++++----- .../construction/simulation/Simulation.kt | 4 +++- .../main/kotlin/com/lambda/task/tasks/BuildTask.kt | 4 ++-- .../kotlin/com/lambda/task/tasks/PlaceContainer.kt | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index cf24bf59b..df60c6997 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.construction.simulation +import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext @@ -70,16 +71,17 @@ object BuildSimulator { interact: InteractionConfig = TaskFlowModule.interact, rotation: RotationConfig = TaskFlowModule.rotation, inventory: InventoryConfig = TaskFlowModule.inventory, + build: BuildConfig = TaskFlowModule.build, ) = runSafe { structure.entries.flatMap { (pos, target) -> - checkRequirements(pos, target)?.let { + checkRequirements(pos, target, build)?.let { return@flatMap setOf(it) } checkPlaceResults(pos, target, eye, interact, rotation, inventory).let { if (it.isEmpty()) return@let return@flatMap it } - checkBreakResults(pos, eye, interact, rotation, inventory).let { + checkBreakResults(pos, eye, interact, rotation, inventory, build).let { if (it.isEmpty()) return@let return@flatMap it } @@ -91,6 +93,7 @@ object BuildSimulator { private fun SafeContext.checkRequirements( pos: BlockPos, target: TargetState, + build: BuildConfig ): BuildResult? {/* the chunk is not loaded */ if (!world.isChunkLoaded(pos)) { return BuildResult.ChunkNotLoaded(pos) @@ -104,7 +107,7 @@ object BuildSimulator { } /* block should be ignored */ - if (state.block in TaskFlowModule.build.ignoredBlocks && target.type == TargetState.Type.AIR) { + if (state.block in build.ignoredBlocks && target.type == TargetState.Type.AIR) { return BuildResult.Ignored(pos) } @@ -314,13 +317,14 @@ object BuildSimulator { eye: Vec3d, interact: InteractionConfig, rotation: RotationConfig, - inventory: InventoryConfig + inventory: InventoryConfig, + build: BuildConfig ): Set { val acc = mutableSetOf() val state = pos.blockState(world) /* is a block that will be destroyed by breaking adjacent blocks */ - if (TaskFlowModule.build.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { + if (build.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { acc.add(BuildResult.Ignored(pos)) return acc } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index 78990bc39..0741b2027 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.construction.simulation +import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext @@ -40,6 +41,7 @@ data class Simulation( val interact: InteractionConfig = TaskFlowModule.interact, val rotation: RotationConfig = TaskFlowModule.rotation, val inventory: InventoryConfig = TaskFlowModule.inventory, + val build: BuildConfig = TaskFlowModule.build, ) { private val cache: MutableMap> = mutableMapOf() private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, ClientPlayerEntity.DEFAULT_EYE_HEIGHT.toDouble(), 0.5) @@ -60,7 +62,7 @@ data class Simulation( ) return@getOrPut emptySet() } - blueprint.simulate(view, interact, rotation, inventory) + blueprint.simulate(view, interact, rotation, inventory, build) } private fun SafeContext.playerFitsIn(pos: Vec3d): Boolean { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index dc20e9ff1..e70ab3d9e 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -60,7 +60,7 @@ class BuildTask @Ta5kBuilder constructor( private val interact: InteractionConfig = TaskFlowModule.interact, private val inventory: InventoryConfig = TaskFlowModule.inventory, ) : Task() { - override val name: String get() = "Building $blueprint with ${(placements / (age / 20.0 + 0.001)).string} p/s" + override val name: String get() = "Building $blueprint with ${(placements / (age / 20.0 + 0.001)).string} p/s ${(breaks / (age / 20.0 + 0.001)).string} p/s" private val pendingPlacements = ConcurrentLinkedQueue() private val pendingBreaks = ConcurrentLinkedQueue() @@ -99,7 +99,7 @@ class BuildTask @Ta5kBuilder constructor( } // ToDo: Simulate for each pair player positions that work - val results = blueprint.simulate(player.eyePos, interact, rotation, inventory) + val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) TaskFlowModule.drawables = results.filterIsInstance().plus(pendingPlacements.toList()) val instantResults = results.filterIsInstance() diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index 1fe67d663..9d17df571 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -48,7 +48,7 @@ class PlaceContainer @Ta5kBuilder constructor( it.blockPos .toStructure(TargetState.Stack(startStack)) .toBlueprint() - .simulate(player.getCameraPosVec(mc.tickDelta)) + .simulate(player.eyePos) } val succeeds = results.filterIsInstance().filter { From 42c1c0ef00306dafaa0082761acb655f59c75a2a Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 19:01:16 +0100 Subject: [PATCH 53/69] Task duration --- common/src/main/kotlin/com/lambda/task/Task.kt | 9 ++++++++- .../src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 3c45fdcbe..fe0abf052 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -28,6 +28,8 @@ import com.lambda.threading.runSafe import com.lambda.util.Communication.logError import com.lambda.util.Nameable import com.lambda.util.StringUtils.capitalize +import kotlin.time.DurationUnit +import kotlin.time.toDuration typealias TaskGenerator = SafeContext.(R) -> Task<*> typealias TaskGeneratorOrNull = SafeContext.(R) -> Task<*>? @@ -306,11 +308,16 @@ abstract class Task : Nameable, Muteable { return this } + val duration: String get() = + (age * 50).toDuration(DurationUnit.MILLISECONDS).toComponents { days, hours, minutes, seconds, nanoseconds -> + "${"%03d".format(days)}:${"%02d".format(hours)}:${"%02d".format(minutes)}:${"%02d".format(seconds)}.${"${nanoseconds / 1_000_000}".take(2)}" + } + override fun toString() = buildString { appendTaskTree(this@Task) } private fun StringBuilder.appendTaskTree(task: Task<*>, level: Int = 0) { - appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is RootTask) " [${task.state.display}]" else "") + appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is RootTask) " ${task.duration} [${task.state.display}]" else "") if (!TaskFlowModule.showAllEntries && (task.state == State.COMPLETED || task.state == State.CANCELLED)) return task.subTasks.forEach { if (!TaskFlowModule.showAllEntries && task is RootTask && (it.state == State.COMPLETED || it.state == State.CANCELLED)) return@forEach diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index e70ab3d9e..3bac74ea2 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -60,7 +60,7 @@ class BuildTask @Ta5kBuilder constructor( private val interact: InteractionConfig = TaskFlowModule.interact, private val inventory: InventoryConfig = TaskFlowModule.inventory, ) : Task() { - override val name: String get() = "Building $blueprint with ${(placements / (age / 20.0 + 0.001)).string} p/s ${(breaks / (age / 20.0 + 0.001)).string} p/s" + override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" private val pendingPlacements = ConcurrentLinkedQueue() private val pendingBreaks = ConcurrentLinkedQueue() From d95eda57696d6a917786a96af409cbf4cab3e38e Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 22:57:18 +0100 Subject: [PATCH 54/69] Fix player collision check for simulation --- .../construction/result/Drawable.kt | 5 +- .../construction/simulation/Simulation.kt | 47 +++++++++++-------- .../rotation/visibilty/VisibilityChecker.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 11 ++++- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt index cec234965..c5fedf9b7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Drawable.kt @@ -56,7 +56,10 @@ interface Drawable { } fun SafeContext.withShape(shape: VoxelShape, offset: BlockPos, color: Color, mask: Int = DirectionMask.ALL) { - if (shape.isEmpty) return + if (shape.isEmpty) { + withBox(Box(offset), color, mask) + return + } shape.boundingBoxes.forEach { box -> withBox(box.offset(offset), color, mask) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index 0741b2027..c1fb10848 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -23,6 +23,7 @@ import com.lambda.config.groups.InventoryConfig import com.lambda.context.SafeContext import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.result.BuildResult +import com.lambda.interaction.construction.result.Drawable import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.module.modules.client.TaskFlowModule @@ -32,9 +33,11 @@ import com.lambda.util.world.FastVector import com.lambda.util.world.toBlockPos import com.lambda.util.world.toVec3d import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d +import java.awt.Color data class Simulation( val blueprint: Blueprint, @@ -47,31 +50,37 @@ data class Simulation( private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, ClientPlayerEntity.DEFAULT_EYE_HEIGHT.toDouble(), 0.5) fun simulate( - pos: FastVector - ) = - cache.getOrPut(pos) { - val view = pos.toView() - runSafe { - if (blueprint.isOutOfBounds(view) && blueprint.getClosestPointTo(view) - .distanceTo(view) > 10.0 - ) return@getOrPut emptySet() - val blockPos = pos.toBlockPos() - if (!playerFitsIn(Vec3d.ofBottomCenter(blockPos))) return@getOrPut emptySet() - if (!blockPos.down().blockState(world) - .isSideSolidFullSquare(world, blockPos, Direction.UP) - ) return@getOrPut emptySet() - } + pos: FastVector, + ) = cache.getOrPut(pos) { + val view = pos.toView() + val isOutOfBounds = blueprint.isOutOfBounds(view) + val isTooFar = blueprint.getClosestPointTo(view).distanceTo(view) > 10.0 + runSafe { + if (isOutOfBounds && isTooFar) return@getOrPut emptySet() + val blockPos = pos.toBlockPos() + val isWalkable = blockPos.down().blockState(world).isSideSolidFullSquare(world, blockPos, Direction.UP) + if (!isWalkable) return@getOrPut emptySet() + if (!playerFitsIn(blockPos)) return@getOrPut emptySet() + } + + blueprint.simulate(view, interact, rotation, inventory, build) + } - blueprint.simulate(view, interact, rotation, inventory, build) + fun goodPositions() = cache.filter { it.value.any { it.rank.ordinal < 4 } }.map { PossiblePos(it.key.toBlockPos()) } + + class PossiblePos(val pos: BlockPos): Drawable { + override fun SafeContext.buildRenderer() { + withBox(Vec3d.ofBottomCenter(pos).playerBox(), Color(0, 255, 0, 50)) } + } - private fun SafeContext.playerFitsIn(pos: Vec3d): Boolean { - val pBox = player.boundingBox - val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) - return world.isSpaceEmpty(aabb.offset(pos)) + private fun SafeContext.playerFitsIn(pos: BlockPos): Boolean { + return world.isSpaceEmpty(Vec3d.ofBottomCenter(pos).playerBox()) } companion object { + fun Vec3d.playerBox(): Box = Box(x - 0.3, y, z - 0.3, x + 0.3, y + 1.8, z + 0.3).contract(1.0E-6) + fun Blueprint.simulation( interact: InteractionConfig = TaskFlowModule.interact, rotation: RotationConfig = TaskFlowModule.rotation, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt index 4367644ea..928d27a5d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt @@ -138,7 +138,7 @@ object VisibilityChecker { ) { excludedSides.forEach { side -> if (excludedSides.isNotEmpty() && side !in excludedSides) return@forEach - val (minX, minY, minZ, maxX, maxY, maxZ) = box.shrink(0.01, 0.01, 0.01).bounds(side) + val (minX, minY, minZ, maxX, maxY, maxZ) = box.contract(1.0E-6).bounds(side) val stepX = (maxX - minX) / resolution val stepY = (maxY - minY) / resolution val stepZ = (maxZ - minZ) / resolution diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 3bac74ea2..b6c1fb4e6 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -47,6 +47,7 @@ import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.info import com.lambda.util.Formatting.string import com.lambda.util.extension.Structure +import com.lambda.util.world.toFastVec import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket import net.minecraft.util.math.BlockPos import java.util.concurrent.ConcurrentLinkedQueue @@ -68,6 +69,7 @@ class BuildTask @Ta5kBuilder constructor( private var currentPlacement: PlaceContext? = null private var placements = 0 private var breaks = 0 + private var goodPositions = setOf() override fun SafeContext.onStart() { (blueprint as? DynamicBlueprint)?.create() @@ -98,9 +100,16 @@ class BuildTask @Ta5kBuilder constructor( return@listen } +// val sim = blueprint.simulation(interact, rotation, inventory) +// BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos -> +// sim.simulate(pos.toFastVec()) +// } + // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) - TaskFlowModule.drawables = results.filterIsInstance().plus(pendingPlacements.toList()) + TaskFlowModule.drawables = results.filterIsInstance() + .plus(pendingPlacements.toList()) +// .plus(sim.goodPositions()) val instantResults = results.filterIsInstance() .filter { it.context.instantBreak } From 30cc651dc81b163d373633b1ac2b9d9589614565 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 6 Feb 2025 23:54:50 +0100 Subject: [PATCH 55/69] Surface scan margin --- .../construction/simulation/Simulation.kt | 3 +- .../rotation/visibilty/VisibilityChecker.kt | 104 ++++++++++++------ .../kotlin/com/lambda/task/tasks/BuildTask.kt | 2 +- 3 files changed, 72 insertions(+), 37 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index c1fb10848..f1b0bb024 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -85,6 +85,7 @@ data class Simulation( interact: InteractionConfig = TaskFlowModule.interact, rotation: RotationConfig = TaskFlowModule.rotation, inventory: InventoryConfig = TaskFlowModule.inventory, - ) = Simulation(this, interact, rotation, inventory) + build: BuildConfig = TaskFlowModule.build, + ) = Simulation(this, interact, rotation, inventory, build) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt index 928d27a5d..72b087b1e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt @@ -136,49 +136,83 @@ object VisibilityChecker { scan: SurfaceScan = SurfaceScan.DEFAULT, check: (Direction, Vec3d) -> Unit, ) { + val margin = 0.1 + excludedSides.forEach { side -> if (excludedSides.isNotEmpty() && side !in excludedSides) return@forEach - val (minX, minY, minZ, maxX, maxY, maxZ) = box.contract(1.0E-6).bounds(side) - val stepX = (maxX - minX) / resolution - val stepY = (maxY - minY) / resolution - val stepZ = (maxZ - minZ) / resolution - - // Determine the bounds to scan based on the axis and mode - val (startX, endX) = if (scan.axis == Direction.Axis.X && stepX != 0.0) { - val centerX = (minX + maxX) / 2 - when (scan.mode) { - ScanMode.GREATER_HALF -> centerX + 0.01 to maxX - ScanMode.LESSER_HALF -> minX to centerX - 0.01 - ScanMode.FULL -> minX to maxX + + val contractedBox = box.contract(1.0E-6) + val (minX, minY, minZ, maxX, maxY, maxZ) = contractedBox.bounds(side) + + // Determine start and end for each axis based on scan configuration + val (startX, endX) = when (scan.axis) { + Direction.Axis.X -> { + val centerX = (minX + maxX) / 2 + when (scan.mode) { + ScanMode.GREATER_HALF -> centerX + 0.01 to maxX + ScanMode.LESSER_HALF -> minX to centerX - 0.01 + ScanMode.FULL -> minX to maxX + } } - } else minX to maxX - - val (startY, endY) = if (scan.axis == Direction.Axis.Y && stepY != 0.0) { - val centerY = (minY + maxY) / 2 - when (scan.mode) { - ScanMode.GREATER_HALF -> centerY + 0.01 to maxY - ScanMode.LESSER_HALF -> minY to centerY - 0.01 - ScanMode.FULL -> minY to maxY + else -> minX to maxX + } + + val (startY, endY) = when (scan.axis) { + Direction.Axis.Y -> { + val centerY = (minY + maxY) / 2 + when (scan.mode) { + ScanMode.GREATER_HALF -> centerY + 0.01 to maxY + ScanMode.LESSER_HALF -> minY to centerY - 0.01 + ScanMode.FULL -> minY to maxY + } } - } else minY to maxY - - val (startZ, endZ) = if (scan.axis == Direction.Axis.Z && stepZ != 0.0) { - val centerZ = (minZ + maxZ) / 2 - when (scan.mode) { - ScanMode.GREATER_HALF -> centerZ + 0.01 to maxZ - ScanMode.LESSER_HALF -> minZ to centerZ - 0.01 - ScanMode.FULL -> minZ to maxZ + else -> minY to maxY + } + + val (startZ, endZ) = when (scan.axis) { + Direction.Axis.Z -> { + val centerZ = (minZ + maxZ) / 2 + when (scan.mode) { + ScanMode.GREATER_HALF -> centerZ + 0.01 to maxZ + ScanMode.LESSER_HALF -> minZ to centerZ - 0.01 + ScanMode.FULL -> minZ to maxZ + } } - } else minZ to maxZ + else -> minZ to maxZ + } + // Apply margin and calculate adjusted step values + val adjustedStartX = startX + margin + val adjustedEndX = (endX - margin).coerceAtLeast(adjustedStartX) + val stepX = if (resolution > 0) (adjustedEndX - adjustedStartX) / resolution else 0.0 + + val adjustedStartY = startY + margin + val adjustedEndY = (endY - margin).coerceAtLeast(adjustedStartY) + val stepY = if (resolution > 0) (adjustedEndY - adjustedStartY) / resolution else 0.0 + + val adjustedStartZ = startZ + margin + val adjustedEndZ = (endZ - margin).coerceAtLeast(adjustedStartZ) + val stepZ = if (resolution > 0) (adjustedEndZ - adjustedStartZ) / resolution else 0.0 + + // Iterate over the adjusted ranges (0..resolution).forEach outer@{ i -> - val x = if (stepX != 0.0) startX + stepX * i else startX - if (x > endX) return@outer + val x = adjustedStartX + stepX * i + if (x > adjustedEndX) return@outer + (0..resolution).forEach inner@{ j -> - val y = if (stepY != 0.0) startY + stepY * j else startY - if (y > endY) return@inner - val z = if (stepZ != 0.0) startZ + stepZ * ((if (stepX != 0.0) j else i)) else startZ - if (z > endZ) return@inner + val y = adjustedStartY + stepY * j + if (y > adjustedEndY) return@inner + + // Determine z based on which axis is being scanned + val z = when (scan.axis) { + Direction.Axis.X, Direction.Axis.Y -> { + adjustedStartZ + stepZ * (if (scan.axis == Direction.Axis.X) j else i) + } + else -> adjustedStartZ + stepZ * j + } + + if (z > adjustedEndZ) return@inner + check(side, Vec3d(x, y, z)) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index b6c1fb4e6..db76e9930 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -154,7 +154,7 @@ class BuildTask @Ta5kBuilder constructor( override val pointSelection = interact.pointSelection override val swingHand = interact.swingHand } - val goal = BuildGoal(blueprint.simulation(interaction, rotation, inventory)) + val goal = BuildGoal(blueprint.simulation(interaction, rotation, inventory, build)) BaritoneUtils.setGoalAndPath(goal) } From 4d63c7997dadc114d3c75d6f0082cd7f8c28047c Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 02:11:09 +0100 Subject: [PATCH 56/69] Move breaking into build task. Item pickup strategy still missing --- .../com/lambda/config/groups/BuildConfig.kt | 5 +- .../com/lambda/config/groups/BuildSettings.kt | 6 +- .../construction/context/BreakContext.kt | 24 ++++++- .../construction/context/BuildContext.kt | 7 +++ .../construction/context/PlaceContext.kt | 9 +-- .../construction/result/BreakResult.kt | 12 +--- .../construction/result/BuildResult.kt | 10 ++- .../construction/result/PlaceResult.kt | 8 ++- .../request/rotation/RotationManager.kt | 2 +- .../rotation/visibilty/VisibilityChecker.kt | 2 +- .../src/main/kotlin/com/lambda/task/Task.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 63 ++++++++++--------- .../util/collections/LimitedDecayQueue.kt | 4 +- 13 files changed, 94 insertions(+), 60 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 5b3aa74b0..65590984f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -24,11 +24,12 @@ interface BuildConfig { val pathing: Boolean val stayInRange: Boolean val collectDrops: Boolean + val maxPendingInteractions: Int + val interactionTimeout: Int // Breaking val rotateForBreak: Boolean val breakConfirmation: Boolean - val maxPendingBreaks: Int val breaksPerTick: Int val breakWeakBlocks: Boolean val forceSilkTouch: Boolean @@ -37,7 +38,5 @@ interface BuildConfig { // Placing val rotateForPlace: Boolean val placeConfirmation: Boolean - val placeTimeout: Int - val maxPendingPlacements: Int val placementsPerTick: Int } diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index cfa945eb9..88251114b 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -34,11 +34,11 @@ class BuildSettings( override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.General } override val stayInRange by c.setting("Stay In Range", true, "Stay in range of blocks") { vis() && page == Page.General && pathing } override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.General } + override val maxPendingInteractions by c.setting("Max Pending Interactions", 1, 1..10, 1, "Dont wait for this many interactions for the server response") { vis() && page == Page.General } // Breaking override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() && page == Page.Break } override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation") { vis() && page == Page.Break } - override val maxPendingBreaks by c.setting("Max Pending Breaks", 1, 1..10, 1, "Maximum pending block breaks") { vis() && page == Page.Break } override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.Break } override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.Break } override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() && page == Page.Break } @@ -47,7 +47,7 @@ class BuildSettings( // Placing override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() && page == Page.Place } override val placeConfirmation by c.setting("Place Confirmation", true, "Wait for block placement confirmation") { vis() && page == Page.Place } - override val placeTimeout by c.setting("Place Timeout", 10, 1..30, 1, "Timeout for block placement in ticks", unit = " ticks") { vis() && page == Page.Place && placeConfirmation } - override val maxPendingPlacements by c.setting("Max Pending Places", 1, 1..10, 1, "Maximum pending block places") { vis() && page == Page.Place } override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() && page == Page.Place } + + override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakConfirmation) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 21fd2900a..4961c2d06 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -17,8 +17,13 @@ package com.lambda.interaction.construction.context +import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.DirectionMask.exclude +import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.threading.runSafe import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.util.Hand @@ -26,6 +31,7 @@ import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d +import java.awt.Color data class BreakContext( override val pov: Vec3d, @@ -35,6 +41,19 @@ data class BreakContext( override var hand: Hand, val instantBreak: Boolean, ) : BuildContext { + override val targetState = TargetState.Air + private val baseColor = Color(222, 0, 0, 25) + private val sideColor = Color(222, 0, 0, 100) + + override fun interact(swingHand: Boolean) { + runSafe { + if (interaction.updateBlockBreakingProgress(result.blockPos, result.side)) { + if (player.isCreative) interaction.blockBreakingCooldown = 0 + if (swingHand) player.swingHand(hand) + } + } + } + override val expectedPos: BlockPos get() = result.blockPos @@ -59,7 +78,10 @@ data class BreakContext( } } - override fun SafeContext.buildRenderer() { + override fun shouldRotate(config: BuildConfig) = config.rotateForBreak + override fun SafeContext.buildRenderer() { + withState(checkedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side)) + withState(checkedState, expectedPos, sideColor, result.side) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index b62e78b53..8d5bab41d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -17,7 +17,9 @@ package com.lambda.interaction.construction.context +import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.result.Drawable +import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.request.rotation.RotationRequest import net.minecraft.block.BlockState import net.minecraft.util.Hand @@ -30,14 +32,19 @@ interface BuildContext : Comparable, Drawable { val result: BlockHitResult val distance: Double val expectedState: BlockState + val targetState: TargetState val expectedPos: BlockPos val checkedState: BlockState val hand: Hand val rotation: RotationRequest + fun interact(swingHand: Boolean) + override fun compareTo(other: BuildContext): Int { return compareBy { it.distance }.compare(this, other) } + + fun shouldRotate(config: BuildConfig): Boolean } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index f30147e52..5cbc04360 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.construction.context +import com.lambda.config.groups.BuildConfig import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude @@ -42,16 +43,15 @@ data class PlaceContext( override val checkedState: BlockState, override val hand: Hand, override val expectedPos: BlockPos, - val targetState: TargetState, + override val targetState: TargetState, val sneak: Boolean, val insideBlock: Boolean, val primeDirection: Direction?, ) : BuildContext { - var placeTick = 0L private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) - fun place(swingHand: Boolean) { + override fun interact(swingHand: Boolean) { runSafe { val actionResult = interaction.interactBlock( player, hand, result @@ -65,7 +65,6 @@ data class PlaceContext( if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) { mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand) } - placeTick = mc.uptimeInTicks } else { warn("Internal interaction failed with $actionResult") } @@ -95,4 +94,6 @@ data class PlaceContext( withState(expectedState, expectedPos, baseColor, DirectionMask.ALL.exclude(result.side.opposite)) withState(expectedState, expectedPos, sideColor, result.side.opposite) } + + override fun shouldRotate(config: BuildConfig) = config.rotateForPlace } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 47749be3c..497a9b624 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -43,18 +43,12 @@ sealed class BreakResult : BuildResult() { */ data class Break( override val blockPos: BlockPos, - val context: BreakContext, - ) : Drawable, Resolvable, BreakResult() { + override val context: BreakContext, + ) : Drawable, Contextual, BreakResult() { override val rank = Rank.BREAK_SUCCESS - private val color = Color(222, 0, 0, 100) - - var collectDrop = false - override val pausesParent get() = collectDrop - - override fun resolve() = BreakBlock(context, collectDrop) override fun SafeContext.buildRenderer() { - withPos(context.expectedPos, color, context.result.side) + with(context) { buildRenderer() } } override fun compareTo(other: ComparableResult): Int { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 972e01aea..ab4ec2bec 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -42,6 +42,10 @@ abstract class BuildResult : ComparableResult, Nameable { open val pausesParent = false override val name: String get() = "${this::class.simpleName} at ${blockPos.toShortString()}" + interface Contextual { + val context: BuildContext + } + /** * The build action is done. */ @@ -197,7 +201,7 @@ abstract class BuildResult : ComparableResult, Nameable { ) : Drawable, Resolvable, BuildResult() { override val name: String get() = "Wrong item ($currentItem) for ${blockPos.toShortString()} need ${neededItem.name.string}" override val rank = Rank.WRONG_ITEM - private val color = Color(3, 252, 169, 100) + private val color = Color(3, 252, 169, 25) override val pausesParent get() = true @@ -233,7 +237,7 @@ abstract class BuildResult : ComparableResult, Nameable { ) : Drawable, Resolvable, BuildResult() { override val name: String get() = "Wrong stack for $blockPos need $neededStack." override val rank = Rank.WRONG_ITEM - private val color = Color(3, 252, 169, 100) + private val color = Color(3, 252, 169, 25) override val pausesParent get() = true @@ -269,7 +273,7 @@ abstract class BuildResult : ComparableResult, Nameable { ) : Navigable, Drawable, BuildResult() { override val name: String get() = "Out of reach at $blockPos." override val rank = Rank.OUT_OF_REACH - private val color = Color(252, 3, 207, 100) + private val color = Color(252, 3, 207, 25) val distance: Double by lazy { misses.minOfOrNull { pov.distanceTo(it) } ?: 0.0 diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 1ff9a7352..891ac1763 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -42,10 +42,14 @@ sealed class PlaceResult : BuildResult() { */ data class Place( override val blockPos: BlockPos, - val context: PlaceContext, - ) : PlaceResult() { + override val context: PlaceContext, + ) : Contextual, Drawable, PlaceResult() { override val rank = Rank.PLACE_SUCCESS + override fun SafeContext.buildRenderer() { + with(context) { buildRenderer() } + } + override fun compareTo(other: ComparableResult): Int { return when (other) { is Place -> context.compareTo(other.context) diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index 37d8f5b7c..fb21e26d8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -98,7 +98,7 @@ object RotationManager : RequestHandler(), Loadable { // Update the current rotation prevRotation = currentRotation - currentRotation = targetRotation.fixSensitivity(prevRotation) + currentRotation = targetRotation/*.fixSensitivity(prevRotation)*/ // Handle LOCK mode if (currentRequest?.mode == RotationMode.Lock) { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt index 72b087b1e..d27b21b73 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt @@ -136,7 +136,7 @@ object VisibilityChecker { scan: SurfaceScan = SurfaceScan.DEFAULT, check: (Direction, Vec3d) -> Unit, ) { - val margin = 0.1 + val margin = 0.01 excludedSides.forEach { side -> if (excludedSides.isNotEmpty() && side !in excludedSides) return@forEach diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index fe0abf052..096a1c2d6 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -317,7 +317,7 @@ abstract class Task : Nameable, Muteable { buildString { appendTaskTree(this@Task) } private fun StringBuilder.appendTaskTree(task: Task<*>, level: Int = 0) { - appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is RootTask) " ${task.duration} [${task.state.display}]" else "") + appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is RootTask) " [${task.state.display}] ${task.duration}" else "") if (!TaskFlowModule.showAllEntries && (task.state == State.COMPLETED || task.state == State.CANCELLED)) return task.subTasks.forEach { if (!TaskFlowModule.showAllEntries && task is RootTask && (it.state == State.COMPLETED || it.state == State.CANCELLED)) return@forEach diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index db76e9930..9ad4c0e3f 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -26,6 +26,7 @@ import com.lambda.context.SafeContext import com.lambda.event.events.MovementEvent import com.lambda.event.events.PacketEvent import com.lambda.event.events.TickEvent +import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.construction.blueprint.Blueprint @@ -33,6 +34,7 @@ import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStruc import com.lambda.interaction.construction.blueprint.DynamicBlueprint import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.result.* import com.lambda.interaction.construction.simulation.BuildGoal @@ -46,6 +48,7 @@ import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.info import com.lambda.util.Formatting.string +import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.extension.Structure import com.lambda.util.world.toFastVec import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket @@ -63,10 +66,11 @@ class BuildTask @Ta5kBuilder constructor( ) : Task() { override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" - private val pendingPlacements = ConcurrentLinkedQueue() - private val pendingBreaks = ConcurrentLinkedQueue() + private val pendingInteractions = LimitedDecayQueue( + build.maxPendingInteractions, build.interactionTimeout * 50L + ) { info("Interaction at ${it.expectedPos.toShortString()} timed out") } + private var currentInteraction: BuildContext? = null - private var currentPlacement: PlaceContext? = null private var placements = 0 private var breaks = 0 private var goodPositions = setOf() @@ -77,18 +81,9 @@ class BuildTask @Ta5kBuilder constructor( init { listen { - pendingPlacements.removeIf { - val timeout = (mc.uptimeInTicks - it.placeTick) > build.placeTimeout - if (timeout) { - info("Placement Timeout of ${it.expectedPos.toShortString()}") - } - timeout - } - - currentPlacement?.let { context -> - if (build.rotateForPlace && !context.rotation.done) return@listen - context.place(interact.swingHand) - pendingPlacements.add(context) + currentInteraction?.let { context -> + if (context.shouldRotate(build) && !context.rotation.done) return@listen + context.interact(interact.swingHand) } } @@ -108,7 +103,7 @@ class BuildTask @Ta5kBuilder constructor( // ToDo: Simulate for each pair player positions that work val results = blueprint.simulate(player.eyePos, interact, rotation, inventory, build) TaskFlowModule.drawables = results.filterIsInstance() - .plus(pendingPlacements.toList()) + .plus(pendingInteractions.toList()) // .plus(sim.goodPositions()) val instantResults = results.filterIsInstance() @@ -118,14 +113,13 @@ class BuildTask @Ta5kBuilder constructor( if (build.breaksPerTick > 1 && instantResults.isNotEmpty()) { instantResults.forEach { - it.resolve().execute(this@BuildTask, pauseParent = false) + it.context.interact(interact.swingHand) } return@listen } val resultsWithoutPending = results.filterNot { result -> - val blockedPositions = pendingPlacements.map { it.expectedPos } - result is PlaceResult.Place && result.context.expectedPos in blockedPositions + result.blockPos in pendingInteractions.map { it.expectedPos } } val bestResult = resultsWithoutPending.minOrNull() ?: return@listen when (bestResult) { @@ -162,19 +156,15 @@ class BuildTask @Ta5kBuilder constructor( if (build.pathing) BaritoneUtils.setGoalAndPath(bestResult.goal) } - is PlaceResult.Place -> { - if (pendingPlacements.size >= build.maxPendingPlacements) return@listen + is BuildResult.Contextual -> { + if (pendingInteractions.size >= build.maxPendingInteractions) return@listen - currentPlacement = bestResult.context + currentInteraction = bestResult.context } is Resolvable -> { LOG.info("Resolving: ${bestResult.name}") - if (bestResult is BreakResult.Break) { - bestResult.collectDrop = collectDrops - } - bestResult.resolve().execute(this@BuildTask, pauseParent = bestResult.pausesParent) } } @@ -182,27 +172,38 @@ class BuildTask @Ta5kBuilder constructor( onRotate { if (!build.rotateForPlace) return@onRotate - val rotateTo = currentPlacement?.rotation ?: return@onRotate + val rotateTo = currentInteraction?.rotation ?: return@onRotate rotation.request(rotateTo) } listen { - val context = currentPlacement ?: return@listen + val context = currentInteraction ?: return@listen + if (context !is PlaceContext) return@listen val hitBlock = context.result.blockPos.blockState(world).block if (hitBlock in BlockUtils.interactionBlacklist) { it.input.sneaking = true } } + listen { event -> + val interaction = currentInteraction ?: return@listen + if (interaction.expectedPos != event.pos) return@listen + currentInteraction = null + pendingInteractions.add(interaction) + } + listen { event -> val packet = event.packet if (packet !is BlockUpdateS2CPacket) return@listen - pendingPlacements.firstOrNull { it.expectedPos == packet.pos }?.let { + pendingInteractions.firstOrNull { it.expectedPos == packet.pos }?.let { if (it.targetState.matches(packet.state, packet.pos, world)) { - pendingPlacements.remove(it) - placements++ + pendingInteractions.remove(it) + when (it) { + is BreakContext -> breaks++ + is PlaceContext -> placements++ + } } } } diff --git a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt index b4c99c0cd..9dceafcb6 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt @@ -27,10 +27,12 @@ import java.util.concurrent.ConcurrentLinkedQueue * @param E The type of elements held in this collection. * @property sizeLimit The maximum number of elements the queue can hold at any given time. * @property maxAge The age (in milliseconds) after which elements are considered expired and are removed from the queue. + * @property onDecay Lambda function that is executed on decay of element [E]. */ class LimitedDecayQueue( private var sizeLimit: Int, private var maxAge: Long, + private val onDecay: (E) -> Unit = {} ) : AbstractMutableCollection() { private val queue: ConcurrentLinkedQueue> = ConcurrentLinkedQueue() @@ -126,7 +128,7 @@ class LimitedDecayQueue( private fun cleanUp() { val now = Instant.now() while (queue.isNotEmpty() && now.minusMillis(maxAge).isAfter(queue.peek().second)) { - queue.poll() + onDecay(queue.poll().first) } } } \ No newline at end of file From 1614f387ce46f16bfb34c779a13d74b95ebbcf6b Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 02:53:22 +0100 Subject: [PATCH 57/69] Distinct block update event for server updates --- .../lambda/mixin/world/ClientWorldMixin.java | 8 ++++ .../com/lambda/mixin/world/WorldMixin.java | 2 +- .../com/lambda/event/events/WorldEvent.kt | 44 ++++++++++++++---- .../graphics/renderer/esp/ChunkedESP.kt | 2 +- .../module/modules/player/PacketMine.kt | 3 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 46 +++++++++---------- 6 files changed, 67 insertions(+), 38 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java b/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java index ba292b187..877548369 100644 --- a/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java +++ b/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java @@ -19,10 +19,13 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.EntityEvent; +import com.lambda.event.events.WorldEvent; import com.lambda.module.modules.render.WorldColors; import com.lambda.util.math.ColorKt; +import net.minecraft.block.BlockState; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -57,4 +60,9 @@ private void getSkyColorInject(Vec3d cameraPos, float tickDelta, CallbackInfoRet cir.setReturnValue(ColorKt.getVec3d(WorldColors.getSkyColor())); } } + + @Inject(method = "handleBlockUpdate", at = @At("HEAD"), cancellable = true) + private void handleBlockUpdateInject(BlockPos pos, BlockState newState, int flags, CallbackInfo ci) { + if (EventFlow.post(new WorldEvent.BlockUpdate.Server(pos, newState)).isCanceled()) ci.cancel(); + } } diff --git a/common/src/main/java/com/lambda/mixin/world/WorldMixin.java b/common/src/main/java/com/lambda/mixin/world/WorldMixin.java index bc809a5e8..a232ebb0e 100644 --- a/common/src/main/java/com/lambda/mixin/world/WorldMixin.java +++ b/common/src/main/java/com/lambda/mixin/world/WorldMixin.java @@ -31,6 +31,6 @@ public abstract class WorldMixin { @Inject(method = "onBlockChanged", at = @At("TAIL")) void onBlockChanged(BlockPos pos, BlockState oldBlock, BlockState newBlock, CallbackInfo ci) { - EventFlow.post(new WorldEvent.BlockChange(pos, oldBlock, newBlock)); + EventFlow.post(new WorldEvent.BlockUpdateClientSide(pos, oldBlock, newBlock)); } } diff --git a/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt b/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt index a853183bd..202ab03f9 100644 --- a/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt @@ -18,7 +18,12 @@ package com.lambda.event.events import com.lambda.event.Event +import com.lambda.event.callback.Cancellable +import com.lambda.event.callback.ICancellable +import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState +import net.minecraft.block.Blocks import net.minecraft.util.math.BlockPos import net.minecraft.util.shape.VoxelShape import net.minecraft.world.chunk.WorldChunk @@ -41,7 +46,7 @@ sealed class WorldEvent { * These events can be used to listen for and respond to changes in the state * of chunks within the game world, providing contextual data for the operations. */ - sealed class ChunkEvent : Event { + sealed class ChunkEvent { /** * Event triggering upon chunk loading */ @@ -59,17 +64,36 @@ sealed class WorldEvent { } /** - * Represents a block state change event within the world. + * Represents events related to block updates in the world. * - * @property pos The position of the block within the world where the change occurred. - * @property oldState The block state prior to the change. - * @property newState The block state after the change. + * This sealed class encapsulates different types of block update events, + * distinguishing between client-side state changes and server-side updates. */ - data class BlockChange( - val pos: BlockPos, - val oldState: BlockState, - val newState: BlockState, - ) : Event + sealed class BlockUpdate { + /** + * Represents a client side block state change event within the world. + * + * @property pos The position of the block within the world where the change occurred. + * @property oldState The block state prior to the change. + * @property newState The block state after the change. + */ + data class Client( + val pos: BlockPos, + val oldState: BlockState, + val newState: BlockState, + ) : Event + + /** + * Represents a server block update event in the world. + * + * @property pos The position of the block in the world. + * @property newState The new state of the block after the update. + */ + data class Server( + val pos: BlockPos, + val newState: BlockState, + ) : ICancellable by Cancellable() + } /** * Represents a collision event in the game world. diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index 8e5919078..b17471abc 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -53,7 +53,7 @@ class ChunkedESP private constructor( } init { - listenConcurrently { event -> + listenConcurrently { event -> world.getWorldChunk(event.pos).renderer.notifyChunks() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 4d2a7afc3..947878925 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -28,7 +28,6 @@ import com.lambda.graphics.renderer.esp.global.DynamicESP import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.collectHitsFor import com.lambda.interaction.request.rotation.visibilty.lookAtBlock import com.lambda.module.Module import com.lambda.module.modules.client.TaskFlowModule @@ -515,7 +514,7 @@ object PacketMine : Module( } } - listen { + listen { currentMiningBlock.forEach { ctx -> ctx?.apply { if (it.pos != pos || !isStateBroken(pos.blockState(world), it.newState)) return@forEach diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 9ad4c0e3f..2bdd0a7e7 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -50,10 +50,10 @@ import com.lambda.util.Communication.info import com.lambda.util.Formatting.string import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.extension.Structure -import com.lambda.util.world.toFastVec +import net.minecraft.block.BlockState import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket +import net.minecraft.network.packet.s2c.play.ChunkDeltaUpdateS2CPacket import net.minecraft.util.math.BlockPos -import java.util.concurrent.ConcurrentLinkedQueue class BuildTask @Ta5kBuilder constructor( private val blueprint: Blueprint, @@ -68,7 +68,7 @@ class BuildTask @Ta5kBuilder constructor( private val pendingInteractions = LimitedDecayQueue( build.maxPendingInteractions, build.interactionTimeout * 50L - ) { info("Interaction at ${it.expectedPos.toShortString()} timed out") } + ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } private var currentInteraction: BuildContext? = null private var placements = 0 @@ -106,16 +106,18 @@ class BuildTask @Ta5kBuilder constructor( .plus(pendingInteractions.toList()) // .plus(sim.goodPositions()) - val instantResults = results.filterIsInstance() - .filter { it.context.instantBreak } - .sorted() - .take(build.breaksPerTick) + if (build.breaksPerTick > 1) { + val instantResults = results.filterIsInstance() + .filter { it.context.instantBreak } + .sorted() + .take(build.breaksPerTick) - if (build.breaksPerTick > 1 && instantResults.isNotEmpty()) { instantResults.forEach { it.context.interact(interact.swingHand) + pendingInteractions.add(it.context) } - return@listen + + if (instantResults.isNotEmpty()) return@listen } val resultsWithoutPending = results.filterNot { result -> @@ -186,24 +188,20 @@ class BuildTask @Ta5kBuilder constructor( } } - listen { event -> - val interaction = currentInteraction ?: return@listen - if (interaction.expectedPos != event.pos) return@listen + listen { event -> + val context = currentInteraction ?: return@listen + if (context.expectedPos != event.pos) return@listen currentInteraction = null - pendingInteractions.add(interaction) + pendingInteractions.add(context) } - listen { event -> - val packet = event.packet - if (packet !is BlockUpdateS2CPacket) return@listen - - pendingInteractions.firstOrNull { it.expectedPos == packet.pos }?.let { - if (it.targetState.matches(packet.state, packet.pos, world)) { - pendingInteractions.remove(it) - when (it) { - is BreakContext -> breaks++ - is PlaceContext -> placements++ - } + listen { event -> + pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { context -> + if (!context.targetState.matches(event.newState, event.pos, world)) return@let + pendingInteractions.remove(context) + when (context) { + is BreakContext -> breaks++ + is PlaceContext -> placements++ } } } From 50654c68ced96eda5f42c14705064bd5b39c5ef6 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 04:01:05 +0100 Subject: [PATCH 58/69] Fix stuff --- common/src/main/java/com/lambda/mixin/world/WorldMixin.java | 2 +- common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/world/WorldMixin.java b/common/src/main/java/com/lambda/mixin/world/WorldMixin.java index a232ebb0e..48d82c2ba 100644 --- a/common/src/main/java/com/lambda/mixin/world/WorldMixin.java +++ b/common/src/main/java/com/lambda/mixin/world/WorldMixin.java @@ -31,6 +31,6 @@ public abstract class WorldMixin { @Inject(method = "onBlockChanged", at = @At("TAIL")) void onBlockChanged(BlockPos pos, BlockState oldBlock, BlockState newBlock, CallbackInfo ci) { - EventFlow.post(new WorldEvent.BlockUpdateClientSide(pos, oldBlock, newBlock)); + EventFlow.post(new WorldEvent.BlockUpdate.Client(pos, oldBlock, newBlock)); } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 2bdd0a7e7..99a44ab7a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -167,7 +167,7 @@ class BuildTask @Ta5kBuilder constructor( is Resolvable -> { LOG.info("Resolving: ${bestResult.name}") - bestResult.resolve().execute(this@BuildTask, pauseParent = bestResult.pausesParent) + bestResult.resolve().execute(this@BuildTask, pauseParent = true) } } } @@ -197,8 +197,8 @@ class BuildTask @Ta5kBuilder constructor( listen { event -> pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { context -> - if (!context.targetState.matches(event.newState, event.pos, world)) return@let pendingInteractions.remove(context) + if (!context.targetState.matches(event.newState, event.pos, world)) return@let when (context) { is BreakContext -> breaks++ is PlaceContext -> placements++ From 51db458dbfc28a5744e5964682f4e9343398e1ed Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 04:49:24 +0100 Subject: [PATCH 59/69] Revert to old surface scan function as new one was flawed and it hurts --- .../config/groups/InteractionSettings.kt | 2 +- .../construction/context/BuildContext.kt | 5 +- .../rotation/visibilty/VisibilityChecker.kt | 104 ++++++------------ .../module/modules/player/HighwayTools.kt | 2 +- .../src/main/kotlin/com/lambda/task/Task.kt | 11 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 8 +- 6 files changed, 49 insertions(+), 83 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index 0c831ef5b..02615efeb 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -58,7 +58,7 @@ class InteractionSettings( override val strictRayCast by c.setting("Strict Raycast", true, "Whether to include the environment to the ray cast context", vis) override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible", vis) override val resolution by c.setting("Resolution", 5, 1..20, 1, "The amount of grid divisions per surface of the hit box", "", vis) - override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The way to select the best point", vis) + override val pointSelection by c.setting("Point Selection", PointSelection.Optimum, "The strategy to select the best hit point", vis) // Swing override val swingHand by c.setting("Swing Hand", true, "Whether to swing hand on interactions", vis) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index 8d5bab41d..e5d5fc6d9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -40,11 +40,10 @@ interface BuildContext : Comparable, Drawable { fun interact(swingHand: Boolean) - override fun compareTo(other: BuildContext): Int { - return compareBy { + override fun compareTo(other: BuildContext) = + compareBy { it.distance }.compare(this, other) - } fun shouldRotate(config: BuildConfig): Boolean } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt index d27b21b73..03b6cd96f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt @@ -136,83 +136,49 @@ object VisibilityChecker { scan: SurfaceScan = SurfaceScan.DEFAULT, check: (Direction, Vec3d) -> Unit, ) { - val margin = 0.01 - excludedSides.forEach { side -> if (excludedSides.isNotEmpty() && side !in excludedSides) return@forEach - - val contractedBox = box.contract(1.0E-6) - val (minX, minY, minZ, maxX, maxY, maxZ) = contractedBox.bounds(side) - - // Determine start and end for each axis based on scan configuration - val (startX, endX) = when (scan.axis) { - Direction.Axis.X -> { - val centerX = (minX + maxX) / 2 - when (scan.mode) { - ScanMode.GREATER_HALF -> centerX + 0.01 to maxX - ScanMode.LESSER_HALF -> minX to centerX - 0.01 - ScanMode.FULL -> minX to maxX - } + val (minX, minY, minZ, maxX, maxY, maxZ) = box.contract(1.0E-3).bounds(side) + val stepX = (maxX - minX) / resolution + val stepY = (maxY - minY) / resolution + val stepZ = (maxZ - minZ) / resolution + + // Determine the bounds to scan based on the axis and mode + val (startX, endX) = if (scan.axis == Direction.Axis.X && stepX != 0.0) { + val centerX = (minX + maxX) / 2 + when (scan.mode) { + ScanMode.GREATER_HALF -> centerX + 0.01 to maxX + ScanMode.LESSER_HALF -> minX to centerX - 0.01 + ScanMode.FULL -> minX to maxX } - else -> minX to maxX - } - - val (startY, endY) = when (scan.axis) { - Direction.Axis.Y -> { - val centerY = (minY + maxY) / 2 - when (scan.mode) { - ScanMode.GREATER_HALF -> centerY + 0.01 to maxY - ScanMode.LESSER_HALF -> minY to centerY - 0.01 - ScanMode.FULL -> minY to maxY - } + } else minX to maxX + + val (startY, endY) = if (scan.axis == Direction.Axis.Y && stepY != 0.0) { + val centerY = (minY + maxY) / 2 + when (scan.mode) { + ScanMode.GREATER_HALF -> centerY + 0.01 to maxY + ScanMode.LESSER_HALF -> minY to centerY - 0.01 + ScanMode.FULL -> minY to maxY } - else -> minY to maxY - } - - val (startZ, endZ) = when (scan.axis) { - Direction.Axis.Z -> { - val centerZ = (minZ + maxZ) / 2 - when (scan.mode) { - ScanMode.GREATER_HALF -> centerZ + 0.01 to maxZ - ScanMode.LESSER_HALF -> minZ to centerZ - 0.01 - ScanMode.FULL -> minZ to maxZ - } + } else minY to maxY + + val (startZ, endZ) = if (scan.axis == Direction.Axis.Z && stepZ != 0.0) { + val centerZ = (minZ + maxZ) / 2 + when (scan.mode) { + ScanMode.GREATER_HALF -> centerZ + 0.01 to maxZ + ScanMode.LESSER_HALF -> minZ to centerZ - 0.01 + ScanMode.FULL -> minZ to maxZ } - else -> minZ to maxZ - } + } else minZ to maxZ - // Apply margin and calculate adjusted step values - val adjustedStartX = startX + margin - val adjustedEndX = (endX - margin).coerceAtLeast(adjustedStartX) - val stepX = if (resolution > 0) (adjustedEndX - adjustedStartX) / resolution else 0.0 - - val adjustedStartY = startY + margin - val adjustedEndY = (endY - margin).coerceAtLeast(adjustedStartY) - val stepY = if (resolution > 0) (adjustedEndY - adjustedStartY) / resolution else 0.0 - - val adjustedStartZ = startZ + margin - val adjustedEndZ = (endZ - margin).coerceAtLeast(adjustedStartZ) - val stepZ = if (resolution > 0) (adjustedEndZ - adjustedStartZ) / resolution else 0.0 - - // Iterate over the adjusted ranges (0..resolution).forEach outer@{ i -> - val x = adjustedStartX + stepX * i - if (x > adjustedEndX) return@outer - + val x = if (stepX != 0.0) startX + stepX * i else startX + if (x > endX) return@outer (0..resolution).forEach inner@{ j -> - val y = adjustedStartY + stepY * j - if (y > adjustedEndY) return@inner - - // Determine z based on which axis is being scanned - val z = when (scan.axis) { - Direction.Axis.X, Direction.Axis.Y -> { - adjustedStartZ + stepZ * (if (scan.axis == Direction.Axis.X) j else i) - } - else -> adjustedStartZ + stepZ * j - } - - if (z > adjustedEndZ) return@inner - + val y = if (stepY != 0.0) startY + stepY * j else startY + if (y > endY) return@inner + val z = if (stepZ != 0.0) startZ + stepZ * ((if (stepX != 0.0) j else i)) else startZ + if (z > endZ) return@inner check(side, Vec3d(x, y, z)) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index eab5f1f07..35b2c2bdc 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -67,7 +67,7 @@ object HighwayTools : Module( private val build = BuildSettings(this) { page == Page.Build } private val rotation = RotationSettings(this) { page == Page.Rotation } - private val interact = InteractionSettings(this, InteractionMask.BOTH) { page == Page.Interaction } + private val interact = InteractionSettings(this, InteractionMask.BLOCK) { page == Page.Interaction } private val inventory = InventorySettings(this) { page == Page.Inventory } private var octant = EightWayDirection.NORTH diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 096a1c2d6..4669610c5 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -45,8 +45,6 @@ abstract class Task : Nameable, Muteable { val isCompleted get() = state == State.COMPLETED val size: Int get() = subTasks.sumOf { it.size } + 1 - open var unpausable = false - private var nextTask: TaskGenerator? = null private var nextTaskOrNull: TaskGeneratorOrNull? = null private var onFinish: TaskGeneratorUnit? = null @@ -115,9 +113,9 @@ abstract class Task : Nameable, Muteable { owner.subTasks.add(this) parent = owner LOG.info("${owner.name} started $name") - if (!unpausable || pauseParent) { - LOG.info("$name deactivating parent ${owner.name}") - if (owner !is RootTask) owner.deactivate() + if (pauseParent) { + LOG.info("$name pausing parent ${owner.name}") + if (owner !is RootTask) owner.pause() } state = State.RUNNING runSafe { runCatching { onStart() }.onFailure { failure(it) } } @@ -145,9 +143,8 @@ abstract class Task : Nameable, Muteable { } @Ta5kBuilder - fun deactivate() { + fun pause() { if (state != State.RUNNING) return - if (unpausable) return state = State.PAUSED } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 99a44ab7a..ed0fda39d 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -123,6 +123,8 @@ class BuildTask @Ta5kBuilder constructor( val resultsWithoutPending = results.filterNot { result -> result.blockPos in pendingInteractions.map { it.expectedPos } } + val sortedResults = resultsWithoutPending.sorted() + sortedResults val bestResult = resultsWithoutPending.minOrNull() ?: return@listen when (bestResult) { is BuildResult.Done, @@ -141,7 +143,7 @@ class BuildTask @Ta5kBuilder constructor( // but the player position does not perfectly match the simulated position // hacky fix for now is to walk "closer" but it wont work in every situation val interaction = object : InteractionConfig { - override val attackReach = interact.attackReach + override val attackReach = 3.0 override val interactReach = interact.interactReach - 1 override val scanReach = interact.scanReach override val strictRayCast = interact.strictRayCast @@ -161,6 +163,7 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.Contextual -> { if (pendingInteractions.size >= build.maxPendingInteractions) return@listen +// info("Swapping interaction to ${bestResult.context.expectedPos} ${bestResult.context.distance}") currentInteraction = bestResult.context } @@ -195,7 +198,8 @@ class BuildTask @Ta5kBuilder constructor( pendingInteractions.add(context) } - listen { event -> + listen(alwaysListen = true) { event -> +// info("Update at ${event.pos.toShortString()}: ${event.newState.block.name.string}") pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { context -> pendingInteractions.remove(context) if (!context.targetState.matches(event.newState, event.pos, world)) return@let From 48f1144b8c6df47711bdc4b60d21cee1803a1868 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 05:47:06 +0100 Subject: [PATCH 60/69] Propagating Blueprint --- .../blueprint/PropagatingBlueprint.kt | 53 +++++++++++++++++++ ...ynamicBlueprint.kt => TickingBlueprint.kt} | 20 +++---- .../construction/simulation/BuildSimulator.kt | 2 +- .../module/modules/player/HighwayTools.kt | 52 +++++++++--------- .../com/lambda/module/modules/player/Nuker.kt | 8 ++- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 15 +++--- 6 files changed, 98 insertions(+), 52 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt rename common/src/main/kotlin/com/lambda/interaction/construction/blueprint/{DynamicBlueprint.kt => TickingBlueprint.kt} (74%) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt new file mode 100644 index 000000000..854600855 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/PropagatingBlueprint.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2024 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.interaction.construction.blueprint + +import com.lambda.context.SafeContext +import com.lambda.threading.runSafe +import com.lambda.util.extension.Structure +import net.minecraft.util.math.Vec3i + +data class PropagatingBlueprint( + val onFinish: SafeContext.(Structure) -> Structure = { it }, +) : Blueprint() { + fun next() { + runSafe { + structure = onFinish(structure) + } + } + + override var structure: Structure = emptyMap() + private set(value) { + field = value + bounds.update() + } + + override fun toString() = "Propagating Blueprint at ${center?.toShortString()}" + + companion object { + fun offset(offset: Vec3i): SafeContext.(Structure) -> Structure = { + it.map { (pos, state) -> + pos.add(offset) to state + }.toMap() + } + + fun propagatingBlueprint( + onFinish: SafeContext.(Structure) -> Structure, + ) = PropagatingBlueprint(onFinish) + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/DynamicBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/TickingBlueprint.kt similarity index 74% rename from common/src/main/kotlin/com/lambda/interaction/construction/blueprint/DynamicBlueprint.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/blueprint/TickingBlueprint.kt index df9523f26..67b8a4863 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/DynamicBlueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/TickingBlueprint.kt @@ -22,19 +22,12 @@ import com.lambda.threading.runSafe import com.lambda.util.extension.Structure import net.minecraft.util.math.Vec3i -data class DynamicBlueprint( - val init: SafeContext.(Structure) -> Structure = { emptyMap() }, - val update: SafeContext.(Structure) -> Structure = { it }, +data class TickingBlueprint( + val onTick: SafeContext.(Structure) -> Structure = { it }, ) : Blueprint() { - fun update() { + fun tick() { runSafe { - structure = update(structure) - } - } - - fun create() { - runSafe { - structure = init(structure) + structure = onTick(structure) } } @@ -53,9 +46,8 @@ data class DynamicBlueprint( }.toMap() } - fun Structure.toBlueprint( - init: SafeContext.(Structure) -> Structure = { this@toBlueprint }, + fun tickingBlueprint( onTick: SafeContext.(Structure) -> Structure, - ) = DynamicBlueprint(init = init, update = onTick) + ) = TickingBlueprint(onTick) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index df60c6997..b25e89399 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -150,7 +150,7 @@ object BuildSimulator { val preprocessing = target.findProcessorForState() preprocessing.sides.forEach { neighbor -> - val hitPos = if (targetPosState.isAir) pos.offset(neighbor) else pos + val hitPos = if (targetPosState.isAir || targetPosState.isLiquid) pos.offset(neighbor) else pos val hitSide = neighbor.opposite val voxelShape = hitPos.blockState(world).getOutlineShape(world, hitPos) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 35b2c2bdc..23a34c667 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -21,7 +21,8 @@ import com.lambda.config.groups.BuildSettings import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.InventorySettings import com.lambda.config.groups.RotationSettings -import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.blueprint.Blueprint.Companion.emptyStructure +import com.lambda.interaction.construction.blueprint.PropagatingBlueprint.Companion.propagatingBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -93,7 +94,7 @@ object HighwayTools : Module( octant = player.octant startPos = player.blockPos currentPos = startPos - buildSlice() + buildHighway() } onDisable { runningTask?.cancel() @@ -103,30 +104,29 @@ object HighwayTools : Module( } } - private fun buildSlice() { - distanceMoved += sliceSize - - var structure: Structure = mutableMapOf() - val slice = generateSlice() - repeat(sliceSize) { - val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) - currentPos = currentPos.add(vec) - structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) - } - - runningTask = structure.toBlueprint().build( - build = build, - rotation = rotation, - interact = interact, - inventory = inventory, - ).finally { + private fun buildHighway() { + runningTask = propagatingBlueprint { if (distanceMoved < distance || distance < 0) { - buildSlice() + var structure = emptyStructure() + val slice = generateSlice() + repeat(sliceSize) { + structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) + val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) + currentPos = currentPos.add(vec) + } + distanceMoved += sliceSize + structure } else { this@HighwayTools.info("Highway built") disable() + emptyStructure() } - }.run() + }.build( + build = build, + rotation = rotation, + interact = interact, + inventory = inventory, + ).run() } private fun generateSlice(): Structure { @@ -202,22 +202,24 @@ object HighwayTools : Module( } if (walls != Material.None) { + val wallElevation = rimHeight + if (pavement != Material.None) 1 else 0 + // Left wall structure += generateDirectionalTube( orthogonal, 1, - height, + height - wallElevation, -center + width, - 0, + wallElevation, ).associateWith { target(walls, wallMaterial) } // Right wall structure += generateDirectionalTube( orthogonal, 1, - height, + height - wallElevation, -center - 1, - 0, + wallElevation, ).associateWith { target(walls, wallMaterial) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 3d2e90bd0..c885f7236 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -17,8 +17,7 @@ package com.lambda.module.modules.player -import com.lambda.interaction.construction.blueprint.Blueprint.Companion.emptyStructure -import com.lambda.interaction.construction.blueprint.DynamicBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.blueprint.TickingBlueprint.Companion.tickingBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -44,8 +43,7 @@ object Nuker : Module( init { onEnable { - task = emptyStructure() - .toBlueprint { + task = tickingBlueprint { val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width) .asSequence() .map { it.blockPos } @@ -59,7 +57,7 @@ object Nuker : Module( val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width) .map { it.blockPos } .associateWith { TargetState.Solid } - return@toBlueprint selection + floor + return@tickingBlueprint selection + floor } selection diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index ed0fda39d..2f56d8951 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -24,14 +24,14 @@ import com.lambda.config.groups.InventoryConfig import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.context.SafeContext import com.lambda.event.events.MovementEvent -import com.lambda.event.events.PacketEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.request.rotation.RotationManager.onRotate import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure -import com.lambda.interaction.construction.blueprint.DynamicBlueprint +import com.lambda.interaction.construction.blueprint.PropagatingBlueprint +import com.lambda.interaction.construction.blueprint.TickingBlueprint import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext @@ -50,9 +50,6 @@ import com.lambda.util.Communication.info import com.lambda.util.Formatting.string import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.extension.Structure -import net.minecraft.block.BlockState -import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket -import net.minecraft.network.packet.s2c.play.ChunkDeltaUpdateS2CPacket import net.minecraft.util.math.BlockPos class BuildTask @Ta5kBuilder constructor( @@ -76,7 +73,7 @@ class BuildTask @Ta5kBuilder constructor( private var goodPositions = setOf() override fun SafeContext.onStart() { - (blueprint as? DynamicBlueprint)?.create() + (blueprint as? PropagatingBlueprint)?.next() } init { @@ -88,7 +85,7 @@ class BuildTask @Ta5kBuilder constructor( } listen { - (blueprint as? DynamicBlueprint)?.update() + (blueprint as? TickingBlueprint)?.tick() if (finishOnDone && blueprint.structure.isEmpty()) { failure("Structure is empty") @@ -132,6 +129,10 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.Unbreakable, is BuildResult.Restricted, is BuildResult.NoPermission -> { + if (blueprint is PropagatingBlueprint) { + blueprint.next() + return@listen + } if (finishOnDone) success() } From 4adfb18656beda0e9fd8daf26c20d3b94254ee92 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 05:57:05 +0100 Subject: [PATCH 61/69] Nullable withdrawal or deposit tasks --- .../construction/result/BuildResult.kt | 4 ++-- .../material/container/MaterialContainer.kt | 6 ++--- .../material/transfer/TransferResult.kt | 22 +++++++++++++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index ab4ec2bec..24200e821 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -206,7 +206,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = neededItem.select() - .transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing() + .transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing("Couldn't find ${neededItem.name.string} anywhere.") override fun SafeContext.buildRenderer() { if (blockPos.blockState(world).isAir) { @@ -242,7 +242,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = - neededStack.select().transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing() + neededStack.select().transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing("Couldn't find ${neededStack.item.name.string} anywhere.") override fun SafeContext.buildRenderer() { if (blockPos.blockState(world).isAir) { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index d5fdd33b8..584b0704c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -82,7 +82,7 @@ abstract class MaterialContainer( class Nothing(override val name: String = "Nothing") : Task() { override fun SafeContext.onStart() { - success() + failure(name) } } @@ -90,13 +90,13 @@ abstract class MaterialContainer( * Withdraws items from the container to the player's inventory. */ @Task.Ta5kBuilder - open fun withdraw(selection: StackSelection): Task<*> = Nothing(name) + open fun withdraw(selection: StackSelection): Task<*>? = null /** * Deposits items from the player's inventory into the container. */ @Task.Ta5kBuilder - open fun deposit(selection: StackSelection): Task<*> = Nothing(name) + open fun deposit(selection: StackSelection): Task<*>? = null open fun matchingStacks(selection: StackSelection) = selection.filterStacks(stacks) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt index 0cfef9a6c..776dcdf4e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -31,11 +31,25 @@ abstract class TransferResult : Task() { override val name = "Container Transfer of [$selection] from [${from.name}] to [${to.name}]" override fun SafeContext.onStart() { - from.withdraw(selection).then { - to.deposit(selection).finally { - success() + val withdrawal = from.withdraw(selection) + val deposit = to.deposit(selection) + + val task = when { + withdrawal != null && deposit != null -> { + withdrawal.then { + deposit.finally { success() } + } + } + withdrawal != null -> { + withdrawal.finally { success() } } - }.execute(this@ContainerTransfer) + deposit != null -> { + deposit.finally { success() } + } + else -> null + } + + task?.execute(this@ContainerTransfer) } } From 505ba1d552b724b0d05e3fb1c86704ebc72626e4 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 06:17:34 +0100 Subject: [PATCH 62/69] Sort build results by rotation distance --- .../lambda/interaction/construction/context/BreakContext.kt | 2 +- .../lambda/interaction/construction/context/PlaceContext.kt | 3 +++ .../lambda/interaction/construction/result/BreakResult.kt | 3 +-- .../lambda/interaction/construction/result/BuildResult.kt | 4 ++-- .../interaction/material/container/MaterialContainer.kt | 2 +- .../request/rotation/visibilty/RotationTarget.kt | 6 ++++++ common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt | 1 + 7 files changed, 15 insertions(+), 6 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 4961c2d06..ba5976d4b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -71,7 +71,7 @@ data class BreakContext( override fun compareTo(other: BuildContext): Int { return when (other) { is BreakContext -> compareBy { - it.distance + it.rotation.target.distance }.compare(this, other) else -> 1 diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 5cbc04360..dd0f49308 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -22,6 +22,7 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -81,6 +82,8 @@ data class PlaceContext( it.hand }.thenBy { it.sneak + }.thenBy { + it.rotation.target.distance }.thenBy { it.distance }.thenBy { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 497a9b624..183f27117 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -28,7 +28,6 @@ import com.lambda.interaction.material.container.ContainerManager.findBestAvaila import com.lambda.interaction.material.container.ContainerManager.transfer import com.lambda.interaction.material.container.MaterialContainer import com.lambda.interaction.material.container.containers.MainHandContainer -import com.lambda.task.tasks.BreakBlock import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.util.math.BlockPos @@ -106,7 +105,7 @@ sealed class BreakResult : BuildResult() { ?: selectStack { isItem(badItem).not() }.transfer(MainHandContainer, inventory) - ?: MaterialContainer.Nothing("Couldn't find a tool for ${blockState.block.name.string} with $badItem in main hand.") + ?: MaterialContainer.FailureTask("Couldn't find a tool for ${blockState.block.name.string} with $badItem in main hand.") override fun SafeContext.buildRenderer() { withPos(blockPos, color) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 24200e821..2f892faa0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -206,7 +206,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = neededItem.select() - .transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing("Couldn't find ${neededItem.name.string} anywhere.") + .transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find ${neededItem.name.string} anywhere.") override fun SafeContext.buildRenderer() { if (blockPos.blockState(world).isAir) { @@ -242,7 +242,7 @@ abstract class BuildResult : ComparableResult, Nameable { override val pausesParent get() = true override fun resolve() = - neededStack.select().transfer(MainHandContainer, inventory) ?: MaterialContainer.Nothing("Couldn't find ${neededStack.item.name.string} anywhere.") + neededStack.select().transfer(MainHandContainer, inventory) ?: MaterialContainer.FailureTask("Couldn't find ${neededStack.item.name.string} anywhere.") override fun SafeContext.buildRenderer() { if (blockPos.blockState(world).isAir) { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index 584b0704c..233337b83 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -80,7 +80,7 @@ abstract class MaterialContainer( this.stacks = stacks } - class Nothing(override val name: String = "Nothing") : Task() { + class FailureTask(override val name: String) : Task() { override fun SafeContext.onStart() { failure(name) } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index b4ffc3793..08de0f7b7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -19,6 +19,8 @@ package com.lambda.interaction.request.rotation.visibilty import com.lambda.context.SafeContext import com.lambda.interaction.request.rotation.Rotation +import com.lambda.interaction.request.rotation.Rotation.Companion.dist +import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe @@ -41,6 +43,10 @@ data class RotationTarget( runSafe { buildRotation() } } + val distance by lazy { + runSafe { targetRotation.value?.dist(player.rotation) } ?: 1000.0 + } + /** * Requests a rotation based on the given configuration. * diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 2f56d8951..98d856fd6 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -129,6 +129,7 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.Unbreakable, is BuildResult.Restricted, is BuildResult.NoPermission -> { + if (pendingInteractions.isNotEmpty()) return@listen if (blueprint is PropagatingBlueprint) { blueprint.next() return@listen From db4a5687a975ca2e6b6c6059a8a49eacf3828b1c Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 06:36:22 +0100 Subject: [PATCH 63/69] Cleanup --- common/src/main/kotlin/com/lambda/task/Task.kt | 11 +++++++---- .../main/kotlin/com/lambda/task/tasks/BuildTask.kt | 4 +--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 4669610c5..fce1a3ba4 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -313,11 +313,14 @@ abstract class Task : Nameable, Muteable { override fun toString() = buildString { appendTaskTree(this@Task) } - private fun StringBuilder.appendTaskTree(task: Task<*>, level: Int = 0) { + private fun StringBuilder.appendTaskTree(task: Task<*>, level: Int = 0, maxEntries: Int = 10) { + if (task.state == State.CANCELLED) return appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is RootTask) " [${task.state.display}] ${task.duration}" else "") - if (!TaskFlowModule.showAllEntries && (task.state == State.COMPLETED || task.state == State.CANCELLED)) return - task.subTasks.forEach { - if (!TaskFlowModule.showAllEntries && task is RootTask && (it.state == State.COMPLETED || it.state == State.CANCELLED)) return@forEach + val left = task.subTasks.size - maxEntries + if (left > 0) { + appendLine("${" ".repeat(level * 5)}...and $left more tasks") + } + task.subTasks.takeLast(maxEntries).forEach { appendTaskTree(it, level + 1) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 98d856fd6..3d6ff837d 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -120,8 +120,6 @@ class BuildTask @Ta5kBuilder constructor( val resultsWithoutPending = results.filterNot { result -> result.blockPos in pendingInteractions.map { it.expectedPos } } - val sortedResults = resultsWithoutPending.sorted() - sortedResults val bestResult = resultsWithoutPending.minOrNull() ?: return@listen when (bestResult) { is BuildResult.Done, @@ -172,7 +170,7 @@ class BuildTask @Ta5kBuilder constructor( is Resolvable -> { LOG.info("Resolving: ${bestResult.name}") - bestResult.resolve().execute(this@BuildTask, pauseParent = true) + bestResult.resolve().execute(this@BuildTask) } } } From 6bd7d89bd91bd1124d2dfec8450b693cecb3fd97 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 17:01:41 +0100 Subject: [PATCH 64/69] Indicate non raycasting rotation. Not yet possible bc of internal castings --- .../construction/simulation/BuildSimulator.kt | 20 +++++++++++++++++-- .../request/rotation/RotationRequest.kt | 3 +++ .../rotation/visibilty/RotationTarget.kt | 3 ++- .../src/main/kotlin/com/lambda/task/Task.kt | 2 +- .../kotlin/com/lambda/task/tasks/BuildTask.kt | 4 +--- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index b25e89399..233ecab69 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -35,10 +35,13 @@ import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest +import com.lambda.interaction.request.rotation.visibilty.RequestedHit +import com.lambda.interaction.request.rotation.visibilty.RotationTarget import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces import com.lambda.interaction.request.rotation.visibilty.lookAtBlock +import com.lambda.interaction.request.rotation.visibilty.lookAtHit import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -58,6 +61,7 @@ import net.minecraft.item.ItemUsageContext import net.minecraft.registry.RegistryKeys import net.minecraft.state.property.Properties import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction @@ -180,7 +184,13 @@ object BuildSimulator { val newRotation = eye.rotationTo(vec) - val hit = newRotation.rayCast(interact.interactReach, eye) ?: return@scanSurfaces + val hit = if (interact.strictRayCast) { + newRotation.rayCast(interact.interactReach, eye)?.blockResult + } else { + val hitVec = newRotation.castBox(box, interact.interactReach, eye) + BlockHitResult(hitVec, hitSide, hitPos, false) + } ?: return@scanSurfaces + val checked = CheckedHit(hit, newRotation, interact.interactReach) if (!checked.verify()) return@scanSurfaces @@ -411,7 +421,13 @@ object BuildSimulator { val newRotation = eye.rotationTo(vec) - val hit = newRotation.rayCast(interact.interactReach, eye) ?: return@scanSurfaces + val hit = if (interact.strictRayCast) { + newRotation.rayCast(interact.interactReach, eye)?.blockResult + } else { + val hitVec = newRotation.castBox(box, interact.interactReach, eye) + BlockHitResult(hitVec, side, pos, false) + } ?: return@scanSurfaces + val checked = CheckedHit(hit, newRotation, interact.interactReach) if (!checked.verify()) return@scanSurfaces diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index 2b839a6f8..c4215116a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.request.rotation import com.lambda.interaction.request.Priority import com.lambda.interaction.request.Request +import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.visibilty.RotationTarget import com.lambda.threading.runSafe @@ -42,4 +43,6 @@ data class RotationRequest( mode == RotationMode.None || runSafe { target.verify(target) } == true + + fun megaDone() = target.distance < 0.001 } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index 08de0f7b7..5f8f80a99 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -22,6 +22,7 @@ import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.RotationConfig +import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.collections.resettableLazy @@ -44,7 +45,7 @@ data class RotationTarget( } val distance by lazy { - runSafe { targetRotation.value?.dist(player.rotation) } ?: 1000.0 + runSafe { targetRotation.value?.dist(RotationManager.currentRotation) } ?: 1000.0 } /** diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index fce1a3ba4..de3fd2362 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -318,7 +318,7 @@ abstract class Task : Nameable, Muteable { appendLine("${" ".repeat(level * 4)}${task.name}" + if (task !is RootTask) " [${task.state.display}] ${task.duration}" else "") val left = task.subTasks.size - maxEntries if (left > 0) { - appendLine("${" ".repeat(level * 5)}...and $left more tasks") + appendLine("${" ".repeat((level + 1) * 4)}...and $left more tasks") } task.subTasks.takeLast(maxEntries).forEach { appendTaskTree(it, level + 1) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 3d6ff837d..c28fcabb1 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -79,7 +79,7 @@ class BuildTask @Ta5kBuilder constructor( init { listen { currentInteraction?.let { context -> - if (context.shouldRotate(build) && !context.rotation.done) return@listen + if (context.shouldRotate(build) && !context.rotation.megaDone()) return@listen context.interact(interact.swingHand) } } @@ -163,7 +163,6 @@ class BuildTask @Ta5kBuilder constructor( is BuildResult.Contextual -> { if (pendingInteractions.size >= build.maxPendingInteractions) return@listen -// info("Swapping interaction to ${bestResult.context.expectedPos} ${bestResult.context.distance}") currentInteraction = bestResult.context } @@ -199,7 +198,6 @@ class BuildTask @Ta5kBuilder constructor( } listen(alwaysListen = true) { event -> -// info("Update at ${event.pos.toShortString()}: ${event.newState.block.name.string}") pendingInteractions.firstOrNull { it.expectedPos == event.pos }?.let { context -> pendingInteractions.remove(context) if (!context.targetState.matches(event.newState, event.pos, world)) return@let From f181bd421ca0a3555bed804936cce232bc4581fa Mon Sep 17 00:00:00 2001 From: blade Date: Fri, 7 Feb 2025 23:28:30 +0300 Subject: [PATCH 65/69] Baritone movement fix, surface scan for rotation dsl --- .../construction/simulation/BuildSimulator.kt | 2 +- .../request/rotation/RotationManager.kt | 13 +++--- .../rotation/visibilty/RotationTargets.kt | 6 ++- .../rotation/visibilty/VisibilityChecker.kt | 43 ++++++++++++++++++- .../com/lambda/task/tasks/BreakBlock.kt | 2 +- .../com/lambda/task/tasks/OpenContainer.kt | 2 +- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 233ecab69..9549a4e4d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -291,7 +291,7 @@ object BuildSimulator { eye, blockHit, RotationRequest( - lookAtBlock(blockHit.blockPos, setOf(blockHit.side), interact), rotation + lookAtBlock(blockHit.blockPos, setOf(blockHit.side), config = interact), rotation ), eye.distanceTo(blockHit.pos), resultState, diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt index fb21e26d8..6937f7210 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt @@ -21,10 +21,7 @@ import com.lambda.Lambda import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.EventFlow.post -import com.lambda.event.events.ConnectionEvent -import com.lambda.event.events.PacketEvent -import com.lambda.event.events.PlayerPacketEvent -import com.lambda.event.events.RotationEvent +import com.lambda.event.events.* import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.RequestHandler @@ -197,7 +194,13 @@ object RotationManager : RequestHandler(), Loadable { @JvmStatic fun handleBaritoneRotation(yaw: Float, pitch: Float) { - lookAt(Rotation(yaw, pitch)).requestBy(Baritone.rotation) + baritoneContext = lookAt(Rotation(yaw, pitch)).requestBy(Baritone.rotation) + } + + init { + listenUnsafe { + baritoneContext = null + } } @JvmStatic diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt index f195acbf4..44f0dec78 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTargets.kt @@ -19,6 +19,7 @@ package com.lambda.interaction.request.rotation.visibilty import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext +import com.lambda.interaction.construction.verify.SurfaceScan import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.RotationManager @@ -75,7 +76,7 @@ fun lookAtHit( config: InteractionConfig = TaskFlowModule.interact, ): RotationTarget? { return when (hit) { - is BlockHitResult -> lookAtBlock(hit.blockPos, setOf(hit.side), config) + is BlockHitResult -> lookAtBlock(hit.blockPos, setOf(hit.side), SurfaceScan.DEFAULT, config) is EntityHitResult -> lookAtEntity(hit.entity as? LivingEntity ?: return null, config) else -> null } @@ -101,6 +102,7 @@ fun lookAtEntity( config.attackReach, player.eyePos, ALL_SIDES, + SurfaceScan.DEFAULT, InteractionMask.ENTITY, config ) { requestedHit.verifyHit(hit) }?.targetRotation @@ -119,6 +121,7 @@ fun lookAtEntity( fun lookAtBlock( pos: BlockPos, sides: Set = ALL_SIDES, + surfaceScan: SurfaceScan = SurfaceScan.DEFAULT, config: InteractionConfig = TaskFlowModule.interact, ): RotationTarget { val requestedHit = blockHit(pos, sides, config.interactReach) @@ -129,6 +132,7 @@ fun lookAtBlock( config.interactReach, player.eyePos, sides, + surfaceScan, InteractionMask.BLOCK, config ) { requestedHit.verifyHit(hit) }?.targetRotation diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt index 03b6cd96f..02ec11dca 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/VisibilityChecker.kt @@ -20,6 +20,7 @@ package com.lambda.interaction.request.rotation.visibilty import com.lambda.config.groups.InteractionConfig import com.lambda.config.groups.InteractionSettings import com.lambda.context.SafeContext +import com.lambda.interaction.construction.processing.PreprocessingStep import com.lambda.interaction.construction.verify.ScanMode import com.lambda.interaction.construction.verify.SurfaceScan import com.lambda.interaction.request.rotation.* @@ -27,6 +28,7 @@ import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.util.extension.component6 import com.lambda.util.math.distSq import com.lambda.util.world.raycast.InteractionMask +import net.minecraft.entity.LivingEntity import net.minecraft.util.hit.EntityHitResult import net.minecraft.util.hit.HitResult import net.minecraft.util.math.Box @@ -58,6 +60,7 @@ object VisibilityChecker { reach: Double, eye: Vec3d, sides: Set, + scan: SurfaceScan, targetType: InteractionMask, interaction: InteractionConfig, verify: CheckedHit.() -> Boolean @@ -71,7 +74,7 @@ object VisibilityChecker { } return interaction.pointSelection.select( - collectHitsFor(boxes, reach, eye, sides, SurfaceScan.DEFAULT, targetType, interaction, verify) + collectHitsFor(boxes, reach, eye, sides, scan, targetType, interaction, verify) ) } @@ -119,6 +122,38 @@ object VisibilityChecker { } } + private fun SafeContext.collectHitsInternal( + boxes: List, + reach: Double, + eye: Vec3d, + sides: Set, + scan: SurfaceScan, + targetType: InteractionMask, + entity: LivingEntity?, + interaction: InteractionConfig, + verify: CheckedHit.() -> Boolean, + ) = mutableListOf().apply { + val reachSq = interaction.scanReach.pow(2) + + boxes.forEach { box -> + val visible = visibleSides(box, eye, interaction.checkSideVisibility) + + scanSurfaces(box, visible.intersect(sides), interaction.resolution, scan) { _, vec -> + if (eye distSq vec > reachSq) return@scanSurfaces + + val newRotation = eye.rotationTo(vec) + + val mask = if (interaction.strictRayCast || entity == null) InteractionMask.BOTH else targetType + val hit = newRotation.rayCast(reach, eye, mask = mask) ?: return@scanSurfaces + + val checked = CheckedHit(hit, newRotation, reach) + if (!checked.verify()) return@scanSurfaces + + add(checked) + } + } + } + /** * Scans the surfaces of a given box, optionally excluding specific sides, * and executes a callback for each point calculated based on the scanning parameters. @@ -243,5 +278,9 @@ object VisibilityChecker { val ALL_SIDES = Direction.entries.toSet() - class CheckedHit(val hit: HitResult, val targetRotation: Rotation, val reach: Double) + class CheckedHit( + val hit: HitResult, + val targetRotation: Rotation, + val reach: Double + ) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 299b78055..b7bf49f12 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -100,7 +100,7 @@ class BreakBlock @Ta5kBuilder constructor( } ?: BaritoneUtils.cancel() if (rotate && !ctx.instantBreak && state == State.BREAKING) { - lookAtBlock(blockPos, sides, interact).let { + lookAtBlock(blockPos, sides, config = interact).let { if (it.requestBy(rotation).done) { hitBlock(ctx.result.side) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 3ef9d19bf..9c222b169 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -83,7 +83,7 @@ class OpenContainer @Ta5kBuilder constructor( listen { if (state != State.SCOPING) return@listen - val target = lookAtBlock(blockPos, sides, interact) + val target = lookAtBlock(blockPos, sides, config = interact) if (rotate && !target.requestBy(rotation).done) return@listen val hitResult = target.hit?.hitIfValid()?.blockResult ?: return@listen From a7b7d63eff9cdb3172b29885451f00656ee9d452 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 23:14:17 +0100 Subject: [PATCH 66/69] Fix BuildTask context build timing, block inaccurate pathing positions --- .../construction/context/BuildContext.kt | 6 --- .../construction/simulation/BuildGoal.kt | 16 +++++-- .../construction/simulation/BuildSimulator.kt | 4 +- .../request/rotation/RotationRequest.kt | 2 - .../rotation/visibilty/RotationTarget.kt | 1 - .../kotlin/com/lambda/task/tasks/BuildTask.kt | 45 +++++++------------ 6 files changed, 30 insertions(+), 44 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index e5d5fc6d9..7ff9d5209 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -39,11 +39,5 @@ interface BuildContext : Comparable, Drawable { val rotation: RotationRequest fun interact(swingHand: Boolean) - - override fun compareTo(other: BuildContext) = - compareBy { - it.distance - }.compare(this, other) - fun shouldRotate(config: BuildConfig): Boolean } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt index b1259b228..e4d868965 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildGoal.kt @@ -19,11 +19,19 @@ package com.lambda.interaction.construction.simulation import baritone.api.pathing.goals.Goal import com.lambda.util.world.fastVectorOf +import com.lambda.util.world.toFastVec +import net.minecraft.util.math.BlockPos -class BuildGoal(private val sim: Simulation) : Goal { - override fun isInGoal(x: Int, y: Int, z: Int) = - sim.simulate(fastVectorOf(x, y, z)) - .any { it.rank.ordinal < 4 } +class BuildGoal( + private val sim: Simulation, + blocked: BlockPos +) : Goal { + private val blockedVec = blocked.toFastVec() + + override fun isInGoal(x: Int, y: Int, z: Int): Boolean { + val pos = fastVectorOf(x, y, z) + return sim.simulate(pos).any { it.rank.ordinal < 4 } && blockedVec != pos + } override fun heuristic(x: Int, y: Int, z: Int): Double { val bestRank = sim.simulate(fastVectorOf(x, y, z)) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 9549a4e4d..a19a57192 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -290,9 +290,7 @@ object BuildSimulator { val placeContext = PlaceContext( eye, blockHit, - RotationRequest( - lookAtBlock(blockHit.blockPos, setOf(blockHit.side), config = interact), rotation - ), + RotationRequest(lookAt(checkedHit.targetRotation, 0.001), rotation), eye.distanceTo(blockHit.pos), resultState, blockHit.blockPos.blockState(world), diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt index c4215116a..0525a85ae 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationRequest.kt @@ -43,6 +43,4 @@ data class RotationRequest( mode == RotationMode.None || runSafe { target.verify(target) } == true - - fun megaDone() = target.distance < 0.001 } diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index 5f8f80a99..dc9057c0e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -39,7 +39,6 @@ data class RotationTarget( val verify: RotationTarget.() -> Boolean = { hit?.verifyRotation() ?: true }, private val buildRotation: SafeContext.() -> Rotation?, ) { - val targetRotation = resettableLazy { runSafe { buildRotation() } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index c28fcabb1..51ab9721a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -67,6 +67,7 @@ class BuildTask @Ta5kBuilder constructor( build.maxPendingInteractions, build.interactionTimeout * 50L ) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") } private var currentInteraction: BuildContext? = null + private val instantBreaks = mutableSetOf() private var placements = 0 private var breaks = 0 @@ -79,9 +80,14 @@ class BuildTask @Ta5kBuilder constructor( init { listen { currentInteraction?.let { context -> - if (context.shouldRotate(build) && !context.rotation.megaDone()) return@listen + if (context.shouldRotate(build) && !context.rotation.done) return@let context.interact(interact.swingHand) } + instantBreaks.forEach { context -> + context.interact(interact.swingHand) + pendingInteractions.add(context) + } + instantBreaks.clear() } listen { @@ -91,7 +97,9 @@ class BuildTask @Ta5kBuilder constructor( failure("Structure is empty") return@listen } + } + onRotate { // val sim = blueprint.simulation(interact, rotation, inventory) // BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos -> // sim.simulate(pos.toFastVec()) @@ -109,50 +117,33 @@ class BuildTask @Ta5kBuilder constructor( .sorted() .take(build.breaksPerTick) - instantResults.forEach { - it.context.interact(interact.swingHand) - pendingInteractions.add(it.context) - } + instantBreaks.addAll(instantResults.map { it.context }) - if (instantResults.isNotEmpty()) return@listen + if (instantResults.isNotEmpty()) return@onRotate } val resultsWithoutPending = results.filterNot { result -> result.blockPos in pendingInteractions.map { it.expectedPos } } - val bestResult = resultsWithoutPending.minOrNull() ?: return@listen + val bestResult = resultsWithoutPending.minOrNull() ?: return@onRotate when (bestResult) { is BuildResult.Done, is BuildResult.Ignored, is BuildResult.Unbreakable, is BuildResult.Restricted, is BuildResult.NoPermission -> { - if (pendingInteractions.isNotEmpty()) return@listen + if (pendingInteractions.isNotEmpty()) return@onRotate if (blueprint is PropagatingBlueprint) { blueprint.next() - return@listen + return@onRotate } if (finishOnDone) success() } is BuildResult.NotVisible, is PlaceResult.NoIntegrity -> { - if (!build.pathing) return@listen - // ToDo: - // Solve the problem that baritone stops pathing when it thinks it is in a valid goal - // but the player position does not perfectly match the simulated position - // hacky fix for now is to walk "closer" but it wont work in every situation - val interaction = object : InteractionConfig { - override val attackReach = 3.0 - override val interactReach = interact.interactReach - 1 - override val scanReach = interact.scanReach - override val strictRayCast = interact.strictRayCast - override val checkSideVisibility = interact.checkSideVisibility - override val resolution = interact.resolution - override val pointSelection = interact.pointSelection - override val swingHand = interact.swingHand - } - val goal = BuildGoal(blueprint.simulation(interaction, rotation, inventory, build)) + if (!build.pathing) return@onRotate + val goal = BuildGoal(blueprint.simulation(interact, rotation, inventory, build), player.blockPos) BaritoneUtils.setGoalAndPath(goal) } @@ -161,7 +152,7 @@ class BuildTask @Ta5kBuilder constructor( } is BuildResult.Contextual -> { - if (pendingInteractions.size >= build.maxPendingInteractions) return@listen + if (pendingInteractions.size >= build.maxPendingInteractions) return@onRotate currentInteraction = bestResult.context } @@ -172,9 +163,7 @@ class BuildTask @Ta5kBuilder constructor( bestResult.resolve().execute(this@BuildTask) } } - } - onRotate { if (!build.rotateForPlace) return@onRotate val rotateTo = currentInteraction?.rotation ?: return@onRotate From 75965ba3f26b54141ae9e8db864dde0defb8c2ec Mon Sep 17 00:00:00 2001 From: blade Date: Sat, 8 Feb 2025 16:28:38 +0300 Subject: [PATCH 67/69] FakePlayer refactor, ca speed debug --- .../src/main/kotlin/com/lambda/core/Loader.kt | 5 +- .../module/modules/combat/CrystalAura.kt | 38 +++++--- .../module/modules/combat/FakePlayer.kt | 87 ++++++++++++++----- .../lambda/module/modules/movement/Speed.kt | 2 +- .../lambda/module/modules/player/Replay.kt | 3 +- .../com/lambda/util/player/PlayerUtils.kt | 11 ++- 6 files changed, 106 insertions(+), 40 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index f408518d2..6dde94862 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -22,14 +22,13 @@ import com.lambda.Lambda.LOG import com.lambda.util.Communication.ascii import com.lambda.util.reflections.getInstances import kotlin.system.measureTimeMillis -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.Duration.Companion.milliseconds object Loader { private val started = System.currentTimeMillis() val runtime: String - get() = "${(System.currentTimeMillis() - started).toDuration(DurationUnit.MILLISECONDS)}" + get() = "${(System.currentTimeMillis() - started).milliseconds}" private val loadables = getInstances { forPackages("com.lambda") } diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 4ad39bcfb..ecb3491b7 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -39,7 +39,9 @@ import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.threading.runSafeGameScheduled import com.lambda.util.BlockUtils.blockState +import com.lambda.util.Communication.info import com.lambda.util.Timer +import com.lambda.util.collections.LimitedDecayQueue import com.lambda.util.combat.CombatUtils.crystalDamage import com.lambda.util.math.* import com.lambda.util.math.MathUtils.ceilToInt @@ -56,8 +58,7 @@ import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.* import kotlin.concurrent.fixedRateTimer import kotlin.math.max -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.Duration.Companion.milliseconds object CrystalAura : Module( name = "CrystalAura", @@ -69,8 +70,8 @@ object CrystalAura : Module( /* General */ private val placeRange by setting("Place Range", 4.6, 1.0..7.0, 0.1, "Range to place crystals", " blocks") { page == Page.General } private val explodeRange by setting("Explode Range", 3.0, 1.0..7.0, 0.1, "Range to explode crystals", " blocks") { page == Page.General } - private val placeDelay by setting("Place Delay", 50L, 0L..1000L, 5L, "Delay between placement attempts", " ms") { page == Page.General } - private val explodeDelay by setting("Explode Delay", 10L, 0L..1000L, 5L, "Delay between explosion attempts", " ms") { page == Page.General } + private val placeDelay by setting("Place Delay", 50L, 0L..1000L, 1L, "Delay between placement attempts", " ms") { page == Page.General } + private val explodeDelay by setting("Explode Delay", 10L, 0L..1000L, 1L, "Delay between explosion attempts", " ms") { page == Page.General } private val updateMode by setting("Update Mode", UpdateMode.Async) { page == Page.General } private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L, unit = " ms") { page == Page.General && updateMode == UpdateMode.Async } private val maxUpdatesPerFrame by setting("Max Updates Per Frame", 5, 1..20, 1) { page == Page.General && updateMode == UpdateMode.Async } @@ -117,6 +118,8 @@ object CrystalAura : Module( private val predictionTimer = Timer() private var lastEntityId = 0 + private val decay = LimitedDecayQueue(10000, 3000L) + private val collidingOffsets = mutableListOf().apply { for (x in -1..1) { for (z in -1..1) { @@ -140,7 +143,7 @@ object CrystalAura : Module( if (CrystalAura.isDisabled || updateMode != UpdateMode.Async) return@fixedRateTimer runSafe { - // timer may spam faster than main thread computes(game freezes completely at the beginning of the frame) + // timer may spam faster than main thread computes (game freezes completely at the beginning of the frame) if (updatesThisFrame > maxUpdatesPerFrame) return@runSafe updatesThisFrame++ @@ -151,6 +154,19 @@ object CrystalAura : Module( } } + fixedRateTimer( + name = "CA Counter", + daemon = true, + initialDelay = 0L, + period = 1000L + ) { + if (CrystalAura.isDisabled || !debug) return@fixedRateTimer + + runSafeGameScheduled { + info((decay.size.toDouble() * 0.3333).roundToStep(0.1).toString()) + } + } + // Ticking with alignment listen { if (updateMode == UpdateMode.Ticked) tick() @@ -211,7 +227,9 @@ object CrystalAura : Module( val pos = crystal.baseBlockPos // Invalidate crystal entity - blueprint[pos]?.crystal = null + val opportunity = blueprint[pos] ?: return@listen + opportunity.crystal = null + decay += crystal.id } onEnable { @@ -279,7 +297,7 @@ object CrystalAura : Module( } private fun SafeContext.updateBlueprint(target: LivingEntity) = - updateTimer.runIfPassed(updateDelay.toDuration(DurationUnit.MILLISECONDS)) { + updateTimer.runIfPassed(updateDelay.milliseconds) { resetBlueprint() // Build damage info @@ -452,10 +470,10 @@ object CrystalAura : Module( fun place() { if (rotation.rotate && !lookAt(placeRotation).requestBy(rotation).done) return - placeTimer.runSafeIfPassed(placeDelay.toDuration(DurationUnit.MILLISECONDS)) { + placeTimer.runSafeIfPassed(placeDelay.milliseconds) { placeInternal(this@Opportunity, Hand.MAIN_HAND) - if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime.toDuration(DurationUnit.MILLISECONDS), false) { + if (prediction.onPlace) predictionTimer.runIfNotPassed(packetLifetime.milliseconds, false) { val last = lastEntityId repeat(placePredictions) { @@ -475,7 +493,7 @@ object CrystalAura : Module( fun explode() { if (rotation.rotate && !lookAt(placeRotation).requestBy(rotation).done) return - explodeTimer.runSafeIfPassed(explodeDelay.toDuration(DurationUnit.MILLISECONDS)) { + explodeTimer.runSafeIfPassed(explodeDelay.milliseconds) { crystal?.let { crystal -> explodeInternal(crystal.id) explodeTimer.reset() diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index a3d626659..349859704 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -18,19 +18,22 @@ package com.lambda.module.modules.combat import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.http.Method import com.lambda.http.request import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.onShutdown -import com.lambda.threading.runSafeConcurrent +import com.lambda.threading.runGameScheduled +import com.lambda.threading.runSafe import com.lambda.util.player.spawnFakePlayer import com.mojang.authlib.GameProfile import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry import net.minecraft.entity.Entity import java.util.* +import kotlin.concurrent.fixedRateTimer object FakePlayer : Module( name = "FakePlayer", @@ -38,50 +41,90 @@ object FakePlayer : Module( defaultTags = setOf(ModuleTag.COMBAT, ModuleTag.RENDER) ) { private val playerName by setting("Name", "Steve") + private val fetchKey get() = playerName.lowercase() // Nicknames aren't case-sensitive + + private var fakePlayer: OtherClientPlayerEntity? = null; set(value) { + runSafe { + field?.let { + world.removeEntity(it.id, Entity.RemovalReason.DISCARDED) + } + value?.let { + world.addEntity(it) + } + } + + field = value + } - private var fakePlayer: OtherClientPlayerEntity? = null private val nilUuid = UUID(0, 0) + private val cachedProfiles = hashMapOf() init { - onEnable { - fakePlayer?.let { fake -> - // Avoid multiple api requests - if (fake.gameProfile.name == playerName) { - fakePlayer = spawnFakePlayer(fake.gameProfile) - return@onEnable + listen { + fakePlayer = cachedProfiles[fetchKey]?.let { cached -> + // Keep fetched fake player + fakePlayer?.gameProfile?.also { profile -> + if (profile is FetchedGameProfile && profile.name == cached.name) return@let fakePlayer } - } - runSafeConcurrent { - val uuid = - request("https://api.mojang.com/users/profiles/minecraft/$playerName") { + // Spawn fetched fake player + spawnFakePlayer( + profile = cached, + reference = fakePlayer ?: player, + addToWorld = false + ) + } ?: fakePlayer?.takeIf { it.gameProfile.name == playerName } ?: spawnFakePlayer( + // Spawn offline fake player while fetching + profile = GameProfile(nilUuid, playerName), + reference = fakePlayer ?: player, + addToWorld = false + ) + } + + fixedRateTimer( + name = "FakePlayer profile fetcher", + daemon = true, + initialDelay = 0L, + period = 2000L + ) { + cachedProfiles[fetchKey] ?: runSafe { + val requestedProfile = + request("https://api.mojang.com/users/profiles/minecraft/$fetchKey") { method(Method.GET) - }.json().data?.id ?: nilUuid + }.json().data + + val uuid = requestedProfile?.id ?: nilUuid val fetchedProperties = mc.sessionService.fetchProfile(uuid, true)?.profile?.properties - val profile = GameProfile(nilUuid, playerName).apply { + val profile = FetchedGameProfile(nilUuid, playerName).apply { fetchedProperties?.forEach { key, value -> properties.put(key, value) } } - // This is the cache that mc pulls profile data from when it fetches skins. - mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false)) - spawnFakePlayer(profile) + runGameScheduled { + // This is the cache that mc pulls profile data from when it fetches skins. + mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false)) + cachedProfiles[fetchKey] = profile + } } } onDisable { - deletePlayer() + fakePlayer = null + } + + onShutdown { + disable() } - onShutdown { disable() } + listen { + disable() + } listen { disable() } } - private fun deletePlayer() { - fakePlayer?.setRemoved(Entity.RemovalReason.DISCARDED) - } + private class FetchedGameProfile(id: UUID, name: String) : GameProfile(id, name) } diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 139ef7be4..93394908a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -51,7 +51,7 @@ object Speed : Module( @JvmStatic val mode by setting("Mode", Mode.GRIM_STRAFE).apply { onValueChange { _, _ -> - reset() + Speed.reset() } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index 5007831fe..ab67218a5 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -56,6 +56,7 @@ import java.lang.reflect.Type import java.time.format.DateTimeFormatter import kotlin.io.path.pathString import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.DurationUnit import kotlin.time.toDuration @@ -571,7 +572,7 @@ object Replay : Module( val endPos: Vec3d get() = position.lastOrNull() ?: Vec3d.ZERO val pruneTimesave: Duration - get() = (position.findCyclicPaths(5).size * 50L).toDuration(DurationUnit.MILLISECONDS) + get() = (position.findCyclicPaths(5).size * 50L).milliseconds fun duplicate() = Recording( input.take(size).toMutableList(), diff --git a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index c71a44700..66bb329df 100644 --- a/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -5,6 +5,7 @@ import com.mojang.authlib.GameProfile import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry +import net.minecraft.entity.player.PlayerEntity fun SafeContext.copyPlayer(entity: ClientPlayerEntity) = ClientPlayerEntity(mc, world, mc.networkHandler, null, null, entity.isSneaking, entity.isSprinting).apply { @@ -22,15 +23,19 @@ fun SafeContext.copyPlayer(entity: ClientPlayerEntity) = isOnGround = entity.isOnGround } -fun SafeContext.spawnFakePlayer(profile: GameProfile): OtherClientPlayerEntity { +fun SafeContext.spawnFakePlayer( + profile: GameProfile, + reference: PlayerEntity = player, + addToWorld: Boolean = true +): OtherClientPlayerEntity { val entity = OtherClientPlayerEntity(world, profile).apply { - copyFrom(player) + copyFrom(reference) playerListEntry = PlayerListEntry(profile, false) id = -2024 - 4 - 20 } - world.addEntity(entity) + if (addToWorld) world.addEntity(entity) return entity } From 1e73519ecc1285fa0ad4741831f35af31fe8422c Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Feb 2025 19:01:41 +0300 Subject: [PATCH 68/69] resettableLazy() -> updatableLazy() --- .../interaction/construction/blueprint/Blueprint.kt | 6 +++--- .../interaction/construction/context/BreakContext.kt | 2 +- .../interaction/construction/context/PlaceContext.kt | 3 +-- .../request/rotation/visibilty/RotationTarget.kt | 11 +++++------ .../com/lambda/util/collections/UpdatableLazy.kt | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt index 9007c5efc..0b3133c20 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/blueprint/Blueprint.kt @@ -19,7 +19,7 @@ package com.lambda.interaction.construction.blueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.BlockUtils.blockPos -import com.lambda.util.collections.resettableLazy +import com.lambda.util.collections.updatableLazy import com.lambda.util.extension.Structure import com.lambda.util.math.roundedBlockPos import net.minecraft.structure.StructureTemplate @@ -28,8 +28,8 @@ import net.minecraft.util.math.* abstract class Blueprint { abstract val structure: Structure - val bounds = resettableLazy { - if (structure.isEmpty()) return@resettableLazy null + val bounds = updatableLazy { + if (structure.isEmpty()) return@updatableLazy null val maxX = structure.keys.maxOf { it.x } val maxY = structure.keys.maxOf { it.y } val maxZ = structure.keys.maxOf { it.z } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index ba5976d4b..eb7b15677 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -71,7 +71,7 @@ data class BreakContext( override fun compareTo(other: BuildContext): Int { return when (other) { is BreakContext -> compareBy { - it.rotation.target.distance + it.rotation.target.angleDistance }.compare(this, other) else -> 1 diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index dd0f49308..d76beb3a3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -22,7 +22,6 @@ import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.request.rotation.Rotation.Companion.dist import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -83,7 +82,7 @@ data class PlaceContext( }.thenBy { it.sneak }.thenBy { - it.rotation.target.distance + it.rotation.target.angleDistance }.thenBy { it.distance }.thenBy { diff --git a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt index dc9057c0e..f77f2915a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt +++ b/common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/RotationTarget.kt @@ -20,12 +20,11 @@ package com.lambda.interaction.request.rotation.visibilty import com.lambda.context.SafeContext import com.lambda.interaction.request.rotation.Rotation import com.lambda.interaction.request.rotation.Rotation.Companion.dist -import com.lambda.interaction.request.rotation.Rotation.Companion.rotation import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest import com.lambda.threading.runSafe -import com.lambda.util.collections.resettableLazy +import com.lambda.util.collections.updatableLazy /** * Represents a target for rotation. @@ -39,13 +38,13 @@ data class RotationTarget( val verify: RotationTarget.() -> Boolean = { hit?.verifyRotation() ?: true }, private val buildRotation: SafeContext.() -> Rotation?, ) { - val targetRotation = resettableLazy { + val targetRotation = updatableLazy { runSafe { buildRotation() } } - val distance by lazy { - runSafe { targetRotation.value?.dist(RotationManager.currentRotation) } ?: 1000.0 - } + val angleDistance get() = runSafe { + targetRotation.value?.dist(RotationManager.currentRotation) + } ?: 1000.0 /** * Requests a rotation based on the given configuration. diff --git a/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt b/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt index f5a18a074..f70260392 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/UpdatableLazy.kt @@ -68,4 +68,4 @@ class UpdatableLazy(private val initializer: () -> T) { * @param initializer A lambda function that defines how the value should be computed. * @return An `UpdatableLazy` instance capable of managing a lazily-initialized value. */ -fun resettableLazy(initializer: () -> T) = UpdatableLazy(initializer) \ No newline at end of file +fun updatableLazy(initializer: () -> T) = UpdatableLazy(initializer) \ No newline at end of file From 7ca5a366ed66d0b34ebd1d2220f0cf4e7cb5c838 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 8 Feb 2025 15:23:31 +0100 Subject: [PATCH 69/69] Bring back placement blocking by entities result --- .../lambda/interaction/construction/result/PlaceResult.kt | 4 ++-- .../interaction/construction/simulation/BuildSimulator.kt | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 891ac1763..b1eb8067e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -84,11 +84,11 @@ sealed class PlaceResult : BuildResult() { } /** - * Represents a scenario where block placement is obstructed by a player. + * Represents a scenario where block placement is obstructed by an entity. * * @property blockPos The position of the block that was attempted to be placed. */ - data class BlockedByPlayer( + data class BlockedByEntity( override val blockPos: BlockPos, ) : Navigable, PlaceResult() { override val rank = Rank.PLACE_BLOCKED_BY_PLAYER diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index a19a57192..e68e29f7e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -35,13 +35,10 @@ import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationConfig import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.RotationRequest -import com.lambda.interaction.request.rotation.visibilty.RequestedHit -import com.lambda.interaction.request.rotation.visibilty.RotationTarget +import com.lambda.interaction.request.rotation.visibilty.* import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.CheckedHit import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.scanSurfaces -import com.lambda.interaction.request.rotation.visibilty.lookAtBlock -import com.lambda.interaction.request.rotation.visibilty.lookAtHit import com.lambda.module.modules.client.TaskFlowModule import com.lambda.threading.runSafe import com.lambda.util.BlockUtils @@ -267,7 +264,7 @@ object BuildSimulator { } val resultState = blockItem.getPlacementState(context) ?: run { -// acc.add(PlaceResult.BlockedByPlayer(pos)) + acc.add(PlaceResult.BlockedByEntity(pos)) return@forEach }