From 3010d83314f7074d26e280c02057dc308bab98c6 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 31 Mar 2024 13:33:47 -0400 Subject: [PATCH 1/9] 400% performance gain --- .../com/lambda/util/collections/Extensions.kt | 21 +++++++++++++++++++ .../com/lambda/util/world/EntityUtils.kt | 5 +++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/util/collections/Extensions.kt diff --git a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt new file mode 100644 index 000000000..4d938d1c7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt @@ -0,0 +1,21 @@ +package com.lambda.util.collections + +/** + * Filters elements of the iterable by their runtime type and a predicate, and adds the matching elements to the specified mutable collection. + * + * This function allows filtering elements of an iterable based on their runtime type and a provided predicate function. + * The elements that match both the type constraint and the predicate are added to the destination mutable collection. + * Because we do not want additional overhead, this function acts as pointer receiver to a collection. + * The predicate function determines whether an element should be included based on its type and any additional criteria. + * + * @param R The target type to filter elements to. + * @param C The type of the destination mutable collection. + * @param destination The mutable collection to which the filtered elements will be added. + * @param predicate The predicate function that determines whether an element should be included based on its type and other criteria. + */ +inline fun > Iterable<*>.filterIsInstanceTo( + destination: C, + predicate: (R) -> Boolean +) { + for (element in this) if (element is R && predicate(element)) destination.add(element) +} diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index 3072ec978..f5730c128 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -1,6 +1,7 @@ package com.lambda.util.world import com.lambda.context.SafeContext +import com.lambda.util.collections.filterIsInstanceTo import net.minecraft.entity.Entity import net.minecraft.util.math.ChunkSectionPos import net.minecraft.util.math.Vec3d @@ -48,7 +49,7 @@ object EntityUtils { val sectionY = pos.y.toInt() shr 4 val sectionZ = pos.z.toInt() shr 4 - val entities = mutableListOf() + val entities = ArrayList() // Here we iterate over all sections within the specified distance and add all entities of type [T] to the list. // We do not have to worry about performance here, as the number of sections is very limited. @@ -57,7 +58,7 @@ object EntityUtils { for (y in sectionY - chunks..sectionY + chunks) { for (z in sectionZ - chunks..sectionZ + chunks) { val section = world.entityManager.cache.findTrackingSection(ChunkSectionPos.asLong(x, y, z)) ?: continue - entities.addAll(section.collection.getAllOfType(T::class.java).filter(predicate)) + section.collection.filterIsInstanceTo(entities, predicate) } } } From 26ba17dbcf452cd2c01fb7e0cb685ec535fd2984 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 31 Mar 2024 13:34:26 -0400 Subject: [PATCH 2/9] Feature: Crystal damage utils --- .../com/lambda/util/combat/CrystalUtils.kt | 87 +++++++++++++++++++ .../com/lambda/util/combat/DamageUtils.kt | 35 ++++++++ 2 files changed, 122 insertions(+) create mode 100644 common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/util/combat/DamageUtils.kt diff --git a/common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt b/common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt new file mode 100644 index 000000000..38fc944e7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt @@ -0,0 +1,87 @@ +package com.lambda.util.combat + +import com.lambda.context.SafeContext +import com.lambda.util.combat.DamageUtils.applyProtection +import com.lambda.util.math.VecUtils.minus +import com.lambda.util.math.VecUtils.times +import net.minecraft.enchantment.EnchantmentHelper +import net.minecraft.enchantment.ProtectionEnchantment +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.damage.DamageSource +import net.minecraft.entity.effect.StatusEffects +import net.minecraft.registry.tag.DamageTypeTags +import net.minecraft.server.network.ServerPlayerEntity +import net.minecraft.stat.Stats +import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.MathHelper.clamp +import net.minecraft.util.math.Vec3d +import net.minecraft.world.World.ExplosionSourceType +import net.minecraft.world.explosion.Explosion +import kotlin.math.max + +/** + * Utility functions related to explosion effects and calculations. + */ +object CrystalUtils { + /** + * Calculates the radius of an explosion based on its power. + * @param power The strength of the explosion. + * @return The radius of the explosion. + */ + fun SafeContext.explosionRadius(power: Double): Double = + 1.3 * (power / 0.225) * 0.3 + + /** + * Calculates the damage dealt by an explosion to a living entity. + * @param source The source of the explosion. + * @param entity The entity to calculate the damage for. + * @return The damage dealt by the explosion. + */ + fun SafeContext.explosionDamage(source: Explosion, entity: LivingEntity): Double = + explosionDamage(source.position, entity, source.power.toDouble()) + + /** + * 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. + * @param power The strength of the explosion. + * @return The damage dealt by the explosion. + */ + fun SafeContext.explosionDamage(position: Vec3d, entity: LivingEntity, power: Double): Double { + val distance = entity.pos.distanceTo(position) + if (explosionRadius(power) < distance) return 0.0 // Avoid unnecessary calculations + val size = power * 2.0 + val impact = (1.0 - distance / size) * Explosion.getExposure(position, entity) + val damage = (impact * impact + impact) / 2.0 * 7.0 * size + 1 + return applyProtection(entity, damage, Explosion.createDamageSource(world, null)) + } + + /** + * Calculates the velocity of a living entity affected by an explosion. + * @param entity The entity to calculate the velocity for. + * @param explosion The explosion to calculate the velocity for. + * @return The velocity of the entity. + */ + fun SafeContext.explosionVelocity(entity: LivingEntity, explosion: Explosion): Vec3d = + explosionVelocity(entity, explosion.position, explosion.power.toDouble()) + + /** + * Calculates the velocity of a living entity affected by an explosion. + * @param entity The entity to calculate the velocity for. + * @param position The position of the explosion. + * @param power The strength of the explosion. + * @return The velocity of the entity. + */ + fun SafeContext.explosionVelocity(entity: LivingEntity, position: Vec3d, power: Double): Vec3d { + val distance = entity.pos.distanceTo(position) + if (explosionRadius(power) < distance) return Vec3d.ZERO // Avoid unnecessary calculations + val size = power * 2.0 + val vel = ProtectionEnchantment.transformExplosionKnockback( + entity, + (1.0 - distance / size) * Explosion.getExposure(position, entity) + ) + + val diff = entity.eyePos - position + return diff.normalize() * vel + } +} diff --git a/common/src/main/kotlin/com/lambda/util/combat/DamageUtils.kt b/common/src/main/kotlin/com/lambda/util/combat/DamageUtils.kt new file mode 100644 index 000000000..1a22390ce --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/combat/DamageUtils.kt @@ -0,0 +1,35 @@ +package com.lambda.util.combat + +import com.lambda.context.SafeContext +import net.minecraft.enchantment.EnchantmentHelper +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.damage.DamageSource +import net.minecraft.entity.effect.StatusEffects +import net.minecraft.registry.tag.DamageTypeTags +import kotlin.math.max +import kotlin.math.min + +object DamageUtils { + /** + * @param entity The entity to calculate the damage for + * @param damage The damage to apply + * @return The damage dealt by the explosion + */ + fun SafeContext.applyProtection(entity: LivingEntity, damage: Double, source: DamageSource): Double { + val resistanceAmplifier = entity.getStatusEffect(StatusEffects.RESISTANCE)?.amplifier ?: -1 + + if (source.isIn(DamageTypeTags.BYPASSES_EFFECTS)) return damage + + if (entity.hasStatusEffect(StatusEffects.RESISTANCE) && !source.isIn(DamageTypeTags.BYPASSES_RESISTANCE)) + return (damage - max(damage * (25 - (resistanceAmplifier + 1) * 5) / 25.0, 0.0)).coerceAtLeast(0.0) + + if (source.isIn(DamageTypeTags.BYPASSES_ENCHANTMENTS)) return damage + + val protectionAmount = EnchantmentHelper.getProtectionAmount(entity.armorItems, source) + + if (protectionAmount > 0) return damage * (1.0 - min(protectionAmount, 20) / 25.0) + + return damage + } + +} From a80ff210ba32d1683a1672a78f3db27d835c28b1 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:24:30 -0400 Subject: [PATCH 3/9] Added entity test module --- .../com/lambda/module/modules/EntityTest.kt | 21 +++++++++++++++++++ .../com/lambda/util/world/EntityUtils.kt | 2 -- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/EntityTest.kt diff --git a/common/src/main/kotlin/com/lambda/module/modules/EntityTest.kt b/common/src/main/kotlin/com/lambda/module/modules/EntityTest.kt new file mode 100644 index 000000000..df224646a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/EntityTest.kt @@ -0,0 +1,21 @@ +package com.lambda.module.modules + +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.Module +import com.lambda.util.world.EntityUtils.getClosestEntity +import net.minecraft.entity.Entity + +object EntityTest : Module( + name = "EntityTest", + description = "Test entity", + defaultTags = setOf() +) { + init { + listener { + repeat(10000) { + getClosestEntity(player.eyePos, 7.0) + } + } + } +} diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index f5730c128..60f5e81eb 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -13,8 +13,6 @@ import kotlin.math.ceil */ object EntityUtils { - // TODO: Tick cache implementation - /** * Gets the closest entity of type [T] within a specified range. * From cee341d4282c5b3699c018b9bd2da2ffd7c612be Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:29:18 -0400 Subject: [PATCH 4/9] one liner --- common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index 60f5e81eb..92224b760 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -25,9 +25,7 @@ object EntityUtils { pos: Vec3d, range: Double, noinline predicate: (T) -> Boolean = { true }, - ): T? { - return getFastEntities(pos, range, predicate).firstOrNull { it.pos.squaredDistanceTo(pos) <= range * range } - } + ): T? = getFastEntities(pos, range, predicate).firstOrNull { it.pos.squaredDistanceTo(pos) <= range * range } /** * Gets all entities of type [T] within a specified distance from a position. From c13259e73d2ec8295b1f165f614de6c20735daf9 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 7 Apr 2024 20:55:10 -0400 Subject: [PATCH 5/9] Refactor: Better explosion utils --- .../com/lambda/util/combat/CrystalUtils.kt | 87 ------------ .../util/combat/{DamageUtils.kt => Damage.kt} | 6 +- .../com/lambda/util/combat/Explosion.kt | 131 ++++++++++++++++++ .../src/main/resources/lambda.accesswidener | 1 + 4 files changed, 134 insertions(+), 91 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt rename common/src/main/kotlin/com/lambda/util/combat/{DamageUtils.kt => Damage.kt} (87%) create mode 100644 common/src/main/kotlin/com/lambda/util/combat/Explosion.kt diff --git a/common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt b/common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt deleted file mode 100644 index 38fc944e7..000000000 --- a/common/src/main/kotlin/com/lambda/util/combat/CrystalUtils.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.lambda.util.combat - -import com.lambda.context.SafeContext -import com.lambda.util.combat.DamageUtils.applyProtection -import com.lambda.util.math.VecUtils.minus -import com.lambda.util.math.VecUtils.times -import net.minecraft.enchantment.EnchantmentHelper -import net.minecraft.enchantment.ProtectionEnchantment -import net.minecraft.entity.LivingEntity -import net.minecraft.entity.damage.DamageSource -import net.minecraft.entity.effect.StatusEffects -import net.minecraft.registry.tag.DamageTypeTags -import net.minecraft.server.network.ServerPlayerEntity -import net.minecraft.stat.Stats -import net.minecraft.util.math.MathHelper -import net.minecraft.util.math.MathHelper.clamp -import net.minecraft.util.math.Vec3d -import net.minecraft.world.World.ExplosionSourceType -import net.minecraft.world.explosion.Explosion -import kotlin.math.max - -/** - * Utility functions related to explosion effects and calculations. - */ -object CrystalUtils { - /** - * Calculates the radius of an explosion based on its power. - * @param power The strength of the explosion. - * @return The radius of the explosion. - */ - fun SafeContext.explosionRadius(power: Double): Double = - 1.3 * (power / 0.225) * 0.3 - - /** - * Calculates the damage dealt by an explosion to a living entity. - * @param source The source of the explosion. - * @param entity The entity to calculate the damage for. - * @return The damage dealt by the explosion. - */ - fun SafeContext.explosionDamage(source: Explosion, entity: LivingEntity): Double = - explosionDamage(source.position, entity, source.power.toDouble()) - - /** - * 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. - * @param power The strength of the explosion. - * @return The damage dealt by the explosion. - */ - fun SafeContext.explosionDamage(position: Vec3d, entity: LivingEntity, power: Double): Double { - val distance = entity.pos.distanceTo(position) - if (explosionRadius(power) < distance) return 0.0 // Avoid unnecessary calculations - val size = power * 2.0 - val impact = (1.0 - distance / size) * Explosion.getExposure(position, entity) - val damage = (impact * impact + impact) / 2.0 * 7.0 * size + 1 - return applyProtection(entity, damage, Explosion.createDamageSource(world, null)) - } - - /** - * Calculates the velocity of a living entity affected by an explosion. - * @param entity The entity to calculate the velocity for. - * @param explosion The explosion to calculate the velocity for. - * @return The velocity of the entity. - */ - fun SafeContext.explosionVelocity(entity: LivingEntity, explosion: Explosion): Vec3d = - explosionVelocity(entity, explosion.position, explosion.power.toDouble()) - - /** - * Calculates the velocity of a living entity affected by an explosion. - * @param entity The entity to calculate the velocity for. - * @param position The position of the explosion. - * @param power The strength of the explosion. - * @return The velocity of the entity. - */ - fun SafeContext.explosionVelocity(entity: LivingEntity, position: Vec3d, power: Double): Vec3d { - val distance = entity.pos.distanceTo(position) - if (explosionRadius(power) < distance) return Vec3d.ZERO // Avoid unnecessary calculations - val size = power * 2.0 - val vel = ProtectionEnchantment.transformExplosionKnockback( - entity, - (1.0 - distance / size) * Explosion.getExposure(position, entity) - ) - - val diff = entity.eyePos - position - return diff.normalize() * vel - } -} diff --git a/common/src/main/kotlin/com/lambda/util/combat/DamageUtils.kt b/common/src/main/kotlin/com/lambda/util/combat/Damage.kt similarity index 87% rename from common/src/main/kotlin/com/lambda/util/combat/DamageUtils.kt rename to common/src/main/kotlin/com/lambda/util/combat/Damage.kt index 1a22390ce..e3c31c1c1 100644 --- a/common/src/main/kotlin/com/lambda/util/combat/DamageUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/combat/Damage.kt @@ -1,6 +1,5 @@ package com.lambda.util.combat -import com.lambda.context.SafeContext import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.entity.LivingEntity import net.minecraft.entity.damage.DamageSource @@ -9,13 +8,13 @@ import net.minecraft.registry.tag.DamageTypeTags import kotlin.math.max import kotlin.math.min -object DamageUtils { +object Damage { /** * @param entity The entity to calculate the damage for * @param damage The damage to apply * @return The damage dealt by the explosion */ - fun SafeContext.applyProtection(entity: LivingEntity, damage: Double, source: DamageSource): Double { + fun mask(entity: LivingEntity, damage: Double, source: DamageSource): Double { val resistanceAmplifier = entity.getStatusEffect(StatusEffects.RESISTANCE)?.amplifier ?: -1 if (source.isIn(DamageTypeTags.BYPASSES_EFFECTS)) return damage @@ -31,5 +30,4 @@ object DamageUtils { return damage } - } diff --git a/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt new file mode 100644 index 000000000..4f1f3950d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt @@ -0,0 +1,131 @@ +package com.lambda.util.combat + +import com.lambda.context.SafeContext +import com.lambda.util.math.VecUtils.minus +import com.lambda.util.math.VecUtils.times +import com.lambda.util.world.EntityUtils.getFastEntities +import net.minecraft.enchantment.ProtectionEnchantment +import net.minecraft.entity.LivingEntity +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d +import net.minecraft.world.explosion.Explosion +import kotlin.math.max + +object Explosion { + /** + * Calculates the damage dealt by an explosion to a living entity. + * @param source The source of the explosion. + * @param entity The entity to calculate the damage for. + * @return The damage dealt by the explosion. + */ + fun SafeContext.damage(source: Explosion, entity: LivingEntity) = + damage(source.position, entity, source.power.toDouble()) + + /** + * 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. + * @param power The strength of the explosion above 0. + * @return The damage dealt by the explosion. + */ + fun SafeContext.damage(position: Vec3d, entity: LivingEntity, power: Double): Double { + val distance = entity.pos.distanceTo(position) + + val impact = (1.0 - distance / (power * 2.0)) * + Explosion.getExposure(position, entity) * + 0.4 + + val damage = world.difficulty.id * 3 * + power * + (impact * impact + impact) + 1 + + return Damage.mask(entity, damage, Explosion.createDamageSource(world, null)) + } + + /** + * Calculates the velocity of entities in the explosion. + * @param explosion The explosion to calculate the velocity for. + * @return The velocity of the entities. + */ + fun SafeContext.velocity(explosion: Explosion) = + getFastEntities(explosion.position, explosion.power * 2.0) + .associateWith { entity -> velocity(entity, explosion) } + + /** + * Calculates the velocity of a living entity affected by an explosion. + * @param entity The entity to calculate the velocity for. + * @param explosion The explosion to calculate the velocity for. + * @return The velocity of the entity. + */ + fun SafeContext.velocity(entity: LivingEntity, explosion: Explosion) = + velocity(entity, explosion.position, explosion.power.toDouble()) + + /** + * Calculates the velocity of a living entity affected by an explosion. + * @param entity The entity to calculate the velocity for. + * @param position The position of the explosion. + * @param power The strength of the explosion. + * @return The velocity of the entity. + */ + fun SafeContext.velocity(entity: LivingEntity, position: Vec3d, power: Double): Vec3d { + val distance = entity.pos.distanceTo(position) + + val size = power * 2.0 + val vel = ProtectionEnchantment.transformExplosionKnockback( + entity, + (1.0 - distance / size) * Explosion.getExposure(position, entity) + ) + + val diff = entity.eyePos - position + return diff.normalize() * vel + } + + fun SafeContext.destruction(source: Explosion): List { + val affected = mutableListOf() + + repeat(16) { x -> + repeat(16) { y -> + repeat(16) { z -> + if (x == 0 || x == 15 || y == 0 || y == 15 || z == 0 || z == 15) { + val vec = Vec3d(x / 30.0 - 1.0, y / 30.0 - 1.0, z / 30.0 - 1.0) + val len = vec.length() + + val dx = vec.x / len + val dy = vec.y / len + val dz = vec.z / len + + var explosionX = source.position.x + var explosionY = source.position.y + var explosionZ = source.position.z + + var intensity = source.power * (0.7 + world.random.nextDouble() * 0.6) + + while (intensity > 0) { + val blockPos = BlockPos.ofFloored(explosionX, explosionY, explosionZ) + val block = world.getBlockState(blockPos) + val fluid = world.getFluidState(blockPos) + if (!world.isInBuildLimit(blockPos)) { + break + } + + val resistance = max(block.block.blastResistance, fluid.blastResistance) + intensity -= (resistance + 0.3) * 0.3 + + if (intensity > 0 && source.behavior.canDestroyBlock(source, world, blockPos, block, intensity.toFloat())) { + affected.add(Vec3d(explosionX, explosionY, explosionZ)) + } + + explosionX += dx * 0.3 + explosionY += dy * 0.3 + explosionZ += dz * 0.3 + + intensity -= 0.225 + } + } + } + } + } + + return affected + } +} diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index eba7ff6b3..d98e8944c 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -32,3 +32,4 @@ accessible method net/minecraft/text/Style (Lnet/minecraft/text/TextColor # Other accessible field net/minecraft/client/world/ClientEntityManager cache Lnet/minecraft/world/entity/SectionedEntityCache; accessible field net/minecraft/world/entity/EntityTrackingSection collection Lnet/minecraft/util/collection/TypeFilterableList; +accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; From 5a3aaa0d903a33bb4b08894d102eae1cd70b5c05 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:15:34 -0400 Subject: [PATCH 6/9] Added better kdoc and standard entity search --- .../com/lambda/util/world/EntityUtils.kt | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index 92224b760..4cdf9a3b5 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -8,32 +8,34 @@ import net.minecraft.util.math.Vec3d import kotlin.math.ceil -/** - * Utility class for working with entities in a Minecraft environment. - */ object EntityUtils { - - /** - * Gets the closest entity of type [T] within a specified range. - * - * @param pos The position to search from. - * @param range The maximum distance to search for entities. - * @param predicate Optional predicate to filter entities. - * @return The closest entity of type [T] within the specified range, or null if none is found. - */ - inline fun SafeContext.getClosestEntity( - pos: Vec3d, - range: Double, - noinline predicate: (T) -> Boolean = { true }, - ): T? = getFastEntities(pos, range, predicate).firstOrNull { it.pos.squaredDistanceTo(pos) <= range * range } - /** * Gets all entities of type [T] within a specified distance from a position. * + * This function retrieves entities of type [T] within a specified distance from a given position. It efficiently + * queries nearby chunks based on the distance and returns a list of matching entities, excluding the player entity. + * + * + * Getting all Zombie entities within a certain distance: + * ``` + * val nearbyZombies = getFastEntities(playerPos, 20.0) + * ``` + * + * Getting all hostile entities within a certain distance: + * ``` + * val hostileEntities = getFastEntities(playerPos, 30.0) + * ``` + * This fetches all hostile entities (e.g., Monsters) within a 30-block radius from the player's position. + * + * Please note that this implementation is optimized for performance at small distances. For larger distances, it is + * recommended to use the [getEntities] function instead. + * With the time complexity, we can determine that after 64 blocks, the performance of this function will degrade. + * * @param pos The position to search from. * @param distance The maximum distance to search for entities. - * @param predicate Optional predicate to filter entities. - * @return A list of entities of type [T] within the specified distance from the position. + * @param predicate Optional predicate to filter entities. It allows custom filtering based on entity properties. + * @return A list of entities of type [T] within the specified distance from the position, excluding the player. + * */ inline fun SafeContext.getFastEntities( pos: Vec3d, @@ -61,4 +63,23 @@ object EntityUtils { return entities } + + /** + * Gets all entities of type [T] within a specified distance from a position. + * + * This function retrieves entities of type [T] within a specified distance from a given position. Unlike + * [getFastEntities], it traverses all entities in the world to find matches, while also excluding the player entity. + * + * @param predicate Optional predicate to filter entities. + * @return A list of entities of type [T] within the specified distance from the position without the player. + */ + inline fun SafeContext.getEntities(noinline predicate: (T) -> Boolean = { true }): List { + val entities = ArrayList() + + world.entities.filterIsInstanceTo(entities) { entity -> + entity != player && predicate(entity) + } + + return entities + } } From d64849dc19c01cbfe74ac3db8405b2263365c489 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:31:59 -0400 Subject: [PATCH 7/9] Added closest entity function --- .../com/lambda/util/world/EntityUtils.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index 4cdf9a3b5..47d20c363 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -9,6 +9,28 @@ import kotlin.math.ceil object EntityUtils { + /** + * Gets the closest entity of type [T] within a specified range. + * + * @param pos The position to search from. + * @param range The maximum distance to search for entities. + * @param predicate Optional predicate to filter entities. + * @return The first entity of type [T] that is closest to the position within the specified range. + */ + inline fun SafeContext.getClosestEntity( + pos: Vec3d = player.pos, + range: Double = 6.0, + noinline predicate: (T) -> Boolean = { true }, + ): T? { + + // Speculative execution trolling + val entities = + if (range > 64) getEntities(predicate) + else getFastEntities(pos, range, predicate) + + return entities.minByOrNull { it.squaredDistanceTo(pos) } // There is probably a faster way to do this + } + /** * Gets all entities of type [T] within a specified distance from a position. * From 5a18dd0abe914741e8f87d818a0f74f3d4a35a10 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:31:13 -0400 Subject: [PATCH 8/9] Fix: Entity utils returning player in list --- .../module/modules/combat/CrystalAura.kt | 70 +++++++++++++++++++ .../com/lambda/util/world/EntityUtils.kt | 6 +- 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt 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 new file mode 100644 index 000000000..3a123265a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -0,0 +1,70 @@ +package com.lambda.module.modules.combat + +import com.lambda.config.InteractionSettings +import com.lambda.config.RotationSettings +import com.lambda.event.events.RotationEvent +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.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.Communication.info +import com.lambda.util.combat.Explosion.velocity +import com.lambda.util.world.EntityUtils.getClosestEntity +import com.lambda.util.world.EntityUtils.getFastEntities +import net.minecraft.entity.Entity +import net.minecraft.entity.LivingEntity +import net.minecraft.util.Hand +import net.minecraft.world.explosion.Explosion + +object CrystalAura : Module( + name = "CrystalAura", + description = "Automatically attacks entities with crystals", + defaultTags = setOf(ModuleTag.COMBAT), +) { + private val page by setting("Page", Page.General) + + /* Rotation */ + private val rotation = RotationSettings(this) { page == Page.Targeting } + + /* 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 }) + + /* 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 }) + + /* Rendering */ + + + /* Interaction */ + private val interac = InteractionSettings(this) // Canadian interbank meme + + private enum class Page { + General, Targeting, Placing, Exploding, Rendering + } + + init { + concurrentListener {} + + /*listener { event -> + event.lookAtEntity(rotation, interac, getClosestEntity(player.eyePos, placeRange) ?: return@listener) + }*/ + + listener { + getClosestEntity(player.eyePos, 64.0) + } + } +} diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index 47d20c363..39e8e4468 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -28,7 +28,7 @@ object EntityUtils { if (range > 64) getEntities(predicate) else getFastEntities(pos, range, predicate) - return entities.minByOrNull { it.squaredDistanceTo(pos) } // There is probably a faster way to do this + return entities.minByOrNull { it.squaredDistanceTo(pos) } } /** @@ -78,7 +78,9 @@ object EntityUtils { for (y in sectionY - chunks..sectionY + chunks) { for (z in sectionZ - chunks..sectionZ + chunks) { val section = world.entityManager.cache.findTrackingSection(ChunkSectionPos.asLong(x, y, z)) ?: continue - section.collection.filterIsInstanceTo(entities, predicate) + section.collection.filterIsInstanceTo(entities) { entity -> + entity != player && predicate(entity) + } } } } From 3d3ce9cbf4cd89faf98985af5e5c1b88c2694456 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:39:00 -0400 Subject: [PATCH 9/9] Fix: Search too far --- .../main/kotlin/com/lambda/util/world/EntityUtils.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index 39e8e4468..1cbd18e6a 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -22,10 +22,17 @@ object EntityUtils { range: Double = 6.0, noinline predicate: (T) -> Boolean = { true }, ): T? { - // Speculative execution trolling val entities = if (range > 64) getEntities(predicate) + // I have an idea for optimization. + // + // Since the search operates linearly, eventually it will reach the midpoint. + // Calculate the distance between the first and last entities. + // Obtain the delta value. + // Theoretically, the closest entity should be within a cubic space of delta^3 blocks. + // If there are no entities within this delta box, examine the outer box. (Although this is unlikely given the fact that the closest entity is within the delta box.) + // The performance improvement is relative to the initial state. else getFastEntities(pos, range, predicate) return entities.minByOrNull { it.squaredDistanceTo(pos) } @@ -79,7 +86,7 @@ object EntityUtils { for (z in sectionZ - chunks..sectionZ + chunks) { val section = world.entityManager.cache.findTrackingSection(ChunkSectionPos.asLong(x, y, z)) ?: continue section.collection.filterIsInstanceTo(entities) { entity -> - entity != player && predicate(entity) + entity != player && entity.squaredDistanceTo(pos) <= distance * distance && predicate(entity) } } }