diff --git a/common/src/main/java/com/lambda/mixin/items/TridentMixin.java b/common/src/main/java/com/lambda/mixin/items/TridentMixin.java new file mode 100644 index 000000000..7b4fc5a5d --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/items/TridentMixin.java @@ -0,0 +1,24 @@ +package com.lambda.mixin.items; + +import com.lambda.module.modules.movement.TridentBoost; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import net.minecraft.item.TridentItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(TridentItem.class) +public class TridentMixin { + // Forge doesn't support the @ModityArgs annotation, so we have to chain multiple @ModifyArg + @ModifyArg(method = "onStoppedUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;addVelocity(DDD)V"), index = 0) + private double modifyVelocity0(double velocity) { return TridentBoost.INSTANCE.isEnabled() ? velocity * TridentBoost.INSTANCE.getTridentSpeed() : velocity; } + + @ModifyArg(method = "onStoppedUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;addVelocity(DDD)V"), index = 1) + private double modifyVelocity1(double velocity) { return TridentBoost.INSTANCE.isEnabled() ? velocity * TridentBoost.INSTANCE.getTridentSpeed() : velocity; } + + @ModifyArg(method = "onStoppedUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;addVelocity(DDD)V"), index = 2) + private double modifyVelocity2(double velocity) { return TridentBoost.INSTANCE.isEnabled() ? velocity * TridentBoost.INSTANCE.getTridentSpeed() : velocity; } + + @ModifyExpressionValue(method = {"onStoppedUsing", "use"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isTouchingWaterOrRain()Z")) + private boolean modifyIsTouchingWaterOrRain(boolean original) { return TridentBoost.INSTANCE.isEnabled() ? TridentBoost.INSTANCE.getForceUse() : original; } +} diff --git a/common/src/main/java/com/lambda/mixin/world/WorldMixin.java b/common/src/main/java/com/lambda/mixin/world/WorldMixin.java new file mode 100644 index 000000000..948516ce0 --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/world/WorldMixin.java @@ -0,0 +1,17 @@ +package com.lambda.mixin.world; + +import com.lambda.module.modules.movement.TridentFlight; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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; + +@Mixin(World.class) +public class WorldMixin { + @Inject(method = "hasRain", at = @At("HEAD"), cancellable = true) + private void hasRain(BlockPos pos, CallbackInfoReturnable cir) { + if (TridentFlight.INSTANCE.isEnabled()) cir.setReturnValue(TridentFlight.INSTANCE.getRain()); + } +} diff --git a/common/src/main/kotlin/com/lambda/Lambda.kt b/common/src/main/kotlin/com/lambda/Lambda.kt index 3a861bd35..13822b7da 100644 --- a/common/src/main/kotlin/com/lambda/Lambda.kt +++ b/common/src/main/kotlin/com/lambda/Lambda.kt @@ -9,6 +9,7 @@ import com.lambda.core.Loader import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.tag.ModuleTag import com.lambda.util.KeyCode +import com.mojang.authlib.GameProfile import net.minecraft.block.Block import net.minecraft.client.MinecraftClient import net.minecraft.util.math.BlockPos @@ -32,6 +33,7 @@ object Lambda { .registerTypeAdapter(Color::class.java, ColorSerializer) .registerTypeAdapter(BlockPos::class.java, BlockPosSerializer) .registerTypeAdapter(Block::class.java, BlockSerializer) + .registerTypeAdapter(GameProfile::class.java, GameProfileSerializer) .create() fun initialize() = Loader.initialize() diff --git a/common/src/main/kotlin/com/lambda/config/Configurable.kt b/common/src/main/kotlin/com/lambda/config/Configurable.kt index 00e091f41..f73573004 100644 --- a/common/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/common/src/main/kotlin/com/lambda/config/Configurable.kt @@ -151,13 +151,13 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { * The type parameter [T] must either be a primitive type or a type with a registered type adapter in [Lambda.gson]. * * @param name The unique identifier for the setting. - * @param defaultValue The default [List] value of type [T] for the setting. + * @param defaultValue The default [ArrayList] value of type [T] for the setting. * @param description A brief explanation of the setting's purpose and behavior. * @param visibility A lambda expression that determines the visibility status of the setting. * * ```kotlin * // the parameter type is inferred from the defaultValue - * private val foo by setting("Foo", listOf("bar", "baz")) + * private val foo by setting("Foo", arrayListOf("bar", "baz")) * ``` * * @return The created [ListSetting]. @@ -167,7 +167,7 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { defaultValue: List, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = ListSetting(name, defaultValue, object : TypeToken>() {}.type, description, visibility).also { + ) = ListSetting(name, defaultValue.toMutableList(), TypeToken.getParameterized(MutableList::class.java, T::class.java).type, description, visibility).also { settings.add(it) } @@ -188,12 +188,12 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { * * @return The created [MapSetting]. */ - inline fun setting( + inline fun setting( name: String, defaultValue: Map, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = MapSetting(name, defaultValue, object : TypeToken>() {}.type, description, visibility).also { + ) = MapSetting(name, defaultValue.toMutableMap(), TypeToken.getParameterized(Map::class.java, K::class.java, V::class.java).type, description, visibility).also { settings.add(it) } @@ -219,7 +219,7 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { defaultValue: Set, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = SetSetting(name, defaultValue, object : TypeToken>() {}.type, description, visibility).also { + ) = SetSetting(name, defaultValue.toMutableSet(), TypeToken.getParameterized(Set::class.java, T::class.java).type, description, visibility).also { settings.add(it) } @@ -448,4 +448,4 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { ) = BlockSetting(name, defaultValue, description, visibility).also { settings.add(it) } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/config/Configuration.kt b/common/src/main/kotlin/com/lambda/config/Configuration.kt index 3dc6768a7..6e45e9427 100644 --- a/common/src/main/kotlin/com/lambda/config/Configuration.kt +++ b/common/src/main/kotlin/com/lambda/config/Configuration.kt @@ -99,7 +99,8 @@ abstract class Configuration : Jsonable { this@Configuration.info(message) } .onFailure { - val message = "Failed to load ${configName.capitalize()} config from backup, unrecoverable error" + val message = + "Failed to load ${configName.capitalize()} config from backup, unrecoverable error" LOG.error(message, it) this@Configuration.logError(message) } diff --git a/common/src/main/kotlin/com/lambda/config/configurations/FriendConfig.kt b/common/src/main/kotlin/com/lambda/config/configurations/FriendConfig.kt new file mode 100644 index 000000000..8d80efeaf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/configurations/FriendConfig.kt @@ -0,0 +1,9 @@ +package com.lambda.config.configurations + +import com.lambda.config.Configuration +import com.lambda.util.FolderRegister + +object FriendConfig : Configuration() { + override val configName = "friends" + override val primary = FolderRegister.config.resolve("$configName.json") +} diff --git a/common/src/main/kotlin/com/lambda/config/configurations/ModuleConfig.kt b/common/src/main/kotlin/com/lambda/config/configurations/ModuleConfig.kt index 98965aa36..f87720324 100644 --- a/common/src/main/kotlin/com/lambda/config/configurations/ModuleConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/configurations/ModuleConfig.kt @@ -4,7 +4,6 @@ import com.lambda.config.Configuration import com.lambda.config.configurations.ModuleConfig.configName import com.lambda.config.configurations.ModuleConfig.primary import com.lambda.util.FolderRegister -import java.io.File /** @@ -18,4 +17,4 @@ import java.io.File object ModuleConfig : Configuration() { override val configName = "modules" override val primary = FolderRegister.config.resolve("$configName.json") -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt new file mode 100644 index 000000000..48691d3cc --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt @@ -0,0 +1,34 @@ +package com.lambda.config.serializer + +import com.google.gson.* +import com.mojang.authlib.GameProfile +import java.lang.reflect.Type +import java.util.* + +// Yeah yeah I know, there's already a serializer for GameProfile in the Minecraft codebase. +// But who cares, I'm doing it again. +// What you gon' do bout it, huh? +// That's what I thought. +object GameProfileSerializer : JsonSerializer, JsonDeserializer { + override fun serialize( + src: GameProfile?, + typeOfSrc: Type?, + context: JsonSerializationContext?, + ): JsonElement = + src?.let { + JsonObject().apply { + addProperty("name", it.name) + addProperty("uuid", it.id.toString()) + } + } ?: JsonNull.INSTANCE + + override fun deserialize( + json: JsonElement?, + typeOfT: Type?, + context: JsonDeserializationContext?, + ): GameProfile = + GameProfile( + UUID.fromString(json?.asJsonObject?.get("uuid")?.asString), + json?.asJsonObject?.get("name")?.asString + ) +} diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt index cb6452996..ed100d8c7 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt @@ -7,11 +7,11 @@ import java.lang.reflect.Type class ListSetting( override val name: String, - defaultValue: List, + private val defaultValue: MutableList, private val type: Type, description: String, visibility: () -> Boolean, -) : AbstractSetting>( +) : AbstractSetting>( defaultValue, description, visibility @@ -19,4 +19,9 @@ class ListSetting( override fun loadFromJson(serialized: JsonElement) { value = gson.fromJson(serialized, type) } -} \ No newline at end of file + + override fun toJson(): JsonElement { + value = defaultValue.toMutableList() // Hack the Delegates.observable + return gson.toJsonTree(value) + } +} diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt index a9190f4d5..7ec0492dd 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt @@ -7,11 +7,11 @@ import java.lang.reflect.Type class MapSetting( override val name: String, - defaultValue: Map, + private val defaultValue: MutableMap, private val type: Type, description: String, visibility: () -> Boolean, -) : AbstractSetting>( +) : AbstractSetting>( defaultValue, description, visibility @@ -19,4 +19,9 @@ class MapSetting( override fun loadFromJson(serialized: JsonElement) { value = gson.fromJson(serialized, type) } -} \ No newline at end of file + + override fun toJson(): JsonElement { + value = defaultValue.toMutableMap() // Hack the Delegates.observable + return gson.toJsonTree(value) + } +} diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt index 5bc7b8264..482fbb4a1 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt @@ -7,11 +7,11 @@ import java.lang.reflect.Type class SetSetting( override val name: String, - defaultValue: Set, + private val defaultValue: MutableSet, private val type: Type, description: String, visibility: () -> Boolean, -) : AbstractSetting>( +) : AbstractSetting>( defaultValue, description, visibility @@ -19,4 +19,9 @@ class SetSetting( override fun loadFromJson(serialized: JsonElement) { value = gson.fromJson(serialized, type) } -} \ No newline at end of file + + override fun toJson(): JsonElement { + value = defaultValue.toMutableSet() // Hack the Delegates.observable + return gson.toJsonTree(value) + } +} diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index ff7ab72ef..a5d82ca3d 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -4,6 +4,7 @@ import com.lambda.Lambda import com.lambda.Lambda.LOG import com.lambda.command.CommandManager import com.lambda.config.configurations.GuiConfig +import com.lambda.friend.FriendManager import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.gui.impl.clickgui.GuiConfigurable import com.lambda.gui.impl.clickgui.LambdaClickGui @@ -11,6 +12,8 @@ import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry import com.lambda.util.Communication.ascii +import com.mojang.authlib.GameProfile +import java.util.* import kotlin.system.measureTimeMillis object Loader { @@ -19,7 +22,9 @@ object Loader { CommandManager, RotationManager, PlayerPacketManager, - LambdaFont.Loader + LambdaFont.Loader, + GuiConfigurable, + FriendManager, ) fun initialize() { @@ -38,7 +43,5 @@ object Loader { } LOG.info("${Lambda.MOD_NAME} ${Lambda.VERSION} was successfully initialized (${initTime}ms)") - - GuiConfigurable // ToDo: Find more elegant solution } } diff --git a/common/src/main/kotlin/com/lambda/friend/FriendManager.kt b/common/src/main/kotlin/com/lambda/friend/FriendManager.kt new file mode 100644 index 000000000..9ec8a0ae6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/friend/FriendManager.kt @@ -0,0 +1,39 @@ +package com.lambda.friend + +import com.lambda.config.Configurable +import com.lambda.config.configurations.FriendConfig +import com.lambda.core.Loadable +import com.mojang.authlib.GameProfile +import net.minecraft.server.network.ServerPlayerEntity +import java.util.UUID + +object FriendManager : Configurable(FriendConfig), Loadable { + override val name = "FriendManager" + + private var friends by setting("friends", listOf()) + + fun add(profile: GameProfile) = friends.add(profile) + + fun remove(profile: GameProfile) = friends.remove(profile) + + fun get(name: String) = friends.firstOrNull { it.name == name } + fun get(uuid: UUID) = friends.firstOrNull { it.id == uuid } + + fun contains(profile: GameProfile) = friends.contains(profile) + fun contains(name: String) = friends.any { it.name == name } + fun contains(uuid: UUID) = friends.any { it.id == uuid } + + fun clear() = friends.clear() + + val ServerPlayerEntity.isFriend: Boolean + get() = contains(gameProfile) + + fun ServerPlayerEntity.befriend() = add(gameProfile) + fun ServerPlayerEntity.unfriend() = remove(gameProfile) + + override fun load(): String { + if (friends.isEmpty()) return "No friends loaded, damn bro you don't have to be antisocial online too," + val word = if (friends.size == 1) "friend" else "friends" + return "Loaded ${friends.size} $word." + } +} diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt index 79158c61d..41c6ec57a 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt @@ -2,9 +2,10 @@ package com.lambda.gui.impl.clickgui import com.lambda.config.Configurable import com.lambda.config.configurations.GuiConfig +import com.lambda.core.Loadable import com.lambda.gui.impl.clickgui.windows.TagWindow -object GuiConfigurable : Configurable(GuiConfig) { +object GuiConfigurable : Configurable(GuiConfig), Loadable { override val name = "gui" val windows = setting("windows", listOf(TagWindow())) -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt index f2a44e6e4..6338d8b7d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt @@ -38,8 +38,8 @@ object Packetlogger : Module( private val networkSide by setting("Network Side", NetworkSide.ANY, "Side of the network to log packets from") private val logTicks by setting("Log Ticks", true, "Show game ticks in the log") private val scope by setting("Scope", Scope.ANY, "Scope of packets to log") - private val whitelist by setting("Whitelist Packets", listOf(), "Packets to whitelist") { scope == Scope.WHITELIST } - private val blacklist by setting("Blacklist Packets", listOf(), "Packets to blacklist") { scope == Scope.BLACKLIST } + private val whitelist by setting("Whitelist Packets", arrayListOf(), "Packets to whitelist") { scope == Scope.WHITELIST } + private val blacklist by setting("Blacklist Packets", arrayListOf(), "Packets to blacklist") { scope == Scope.BLACKLIST } private val maxRecursionDepth by setting("Max Recursion Depth", 6, 1..10, 1, "Maximum recursion depth for packet serialization") private val logConcurrent by setting("Build Data Concurrent", false, "Whether to serialize packets concurrently. Will not save packets in chronological order but wont lag the game.") @@ -187,4 +187,4 @@ object Packetlogger : Module( private fun Packet<*>.logSent() { storageFlow.tryEmit("Sent at ${getTime(entryFormatter)}\n${dynamicString(maxRecursionDepth)}\n") } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/HorseUtils.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/HorseUtils.kt new file mode 100644 index 000000000..c5ad344b4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/HorseUtils.kt @@ -0,0 +1,49 @@ +package com.lambda.module.modules.movement + +import com.lambda.event.events.PacketEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.world.EntityUtils.getEntities +import net.minecraft.entity.passive.AbstractHorseEntity +import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket + +object HorseUtils : Module( + name = "HorseUtils", + description = "Various utilities for horses.", + defaultTags = setOf(ModuleTag.MOVEMENT, ModuleTag.BYPASS) +) { + private val page by setting("Page", Page.General) + + /* General */ + private val forceMount by setting("Force Mount", true, description = "Attempts to force mount chested entities.", visibility = { page == Page.General }) + private val tameHorses by setting("Tame Horses", true, description = "Automatically tames horses (client-side only).", visibility = { page == Page.General }) + + /* Rendering */ + private val showInfo by setting("Show Info", true, description = "Renders information about entities.", visibility = { page == Page.Rendering }) + + private val theHonses = mutableListOf() // Petah, the honse is here + private val tame: (AbstractHorseEntity) -> Unit = { horse -> if (tameHorses) horse.setHorseFlag(4, true) } + + private enum class Page { + General, Rendering + } + + init { + listener { + getEntities(theHonses, iterator = tame) + } + + listener { event -> + if (!forceMount) return@listener + if (event.packet !is PlayerInteractEntityC2SPacket) return@listener + if (event.packet.type !is PlayerInteractEntityC2SPacket.InteractAtHandler) return@listener + + val entity = world.getEntityById(event.packet.entityId) ?: return@listener + if (entity !is AbstractHorseEntity) return@listener + + event.cancel() + } + } +} diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/TridentBoost.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/TridentBoost.kt new file mode 100644 index 000000000..0725c5b13 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/TridentBoost.kt @@ -0,0 +1,13 @@ +package com.lambda.module.modules.movement + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag + +object TridentBoost : Module( + name = "TridentBoost", + description = "Boosts you with tridents", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + val tridentSpeed by setting("Speed Factor", 2.0, 0.1..3.0, 0.1, description = "Speed factor of the trident boost") + val forceUse by setting("Force Use", true, description = "Try to use the trident outside of water or rain") +} diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/TridentFlight.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/TridentFlight.kt new file mode 100644 index 000000000..a76273fe3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/TridentFlight.kt @@ -0,0 +1,72 @@ +package com.lambda.module.modules.movement + +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import net.minecraft.enchantment.EnchantmentHelper +import net.minecraft.entity.MovementType +import net.minecraft.item.TridentItem +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.sqrt + + +object TridentFlight : Module( + name = "TridentFlight", + description = "Allows you to fly with tridents", + defaultTags = setOf(ModuleTag.MOVEMENT, ModuleTag.BYPASS, ModuleTag.GRIM), +) { + private val bounce by setting("Bounce", true, description = "Automatically use the trident") + private val delay by setting("Delay", 0, 0..20, 1, description = "Delay in ticks before releasing the trident", visibility = { bounce }) + private val tridentSpeed by setting("Speed Factor", 1.0, 0.1..5.0, 0.1, description = "Speed factor of the trident flight") + + val rain by setting("Rain", true, description = "Set rain client-side to allow flight") + + private var ticks = 0 + + init { + listener { + if (ticks >= delay && + player.activeItem.item is TridentItem) + { + val tridentSlot = player.inventory.selectedSlot + val spoofSlot = player.inventory.swappableHotbarSlot + + connection.sendPacket(UpdateSelectedSlotC2SPacket(tridentSlot)) + connection.sendPacket(PlayerActionC2SPacket(PlayerActionC2SPacket.Action.RELEASE_USE_ITEM, BlockPos.ORIGIN, Direction.DOWN)) + + val level = EnchantmentHelper.getRiptide(player.activeItem) + val yaw = player.yaw * (Math.PI / 180) + val pitch = player.pitch * (Math.PI / 180) + + val x = -sin(yaw) * cos(pitch) + val y = -sin(pitch) + val z = cos(yaw) * cos(pitch) + + val dot = sqrt(x * x + y * y + z * z) + val multiplier = (3 * ((1.0 + level) / 4.0)) / dot + + player.addVelocity( + x * multiplier * tridentSpeed, + y * multiplier * tridentSpeed, + z * multiplier * tridentSpeed + ) + + if (player.isOnGround) + player.move(MovementType.SELF, Vec3d(0.0, 1.19999, 0.0)) + + connection.sendPacket(UpdateSelectedSlotC2SPacket(spoofSlot)) + + ticks = 0 + } + + ticks++ + } + } +} diff --git a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt index 4d938d1c7..90bcf5c4d 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt @@ -14,8 +14,9 @@ package com.lambda.util.collections * @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, + destination: C? = null, predicate: (R) -> Boolean ) { + if (destination == null) return 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 c8c14ca3c..9a938d3ae 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -7,6 +7,7 @@ import net.minecraft.util.math.ChunkSectionPos import net.minecraft.util.math.Vec3d import kotlin.math.ceil +val nullptr = null object EntityUtils { /** @@ -22,20 +23,22 @@ object EntityUtils { range: Double = 6.0, noinline predicate: (T) -> Boolean = { true }, ): T? { + var closest: T? = null + var closestDistance = Double.MAX_VALUE + + val iterator: (T) -> Unit = { + val distance = it.squaredDistanceTo(pos) + if (distance < closestDistance) { + closest = it + closestDistance = distance + } + } + // Speculative execution trolling - val entities = ArrayList() - if (range > 64) getEntities(entities, 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, entities, predicate) + if (range > 64) getEntities(nullptr, predicate, iterator) + else getFastEntities(pos, range, nullptr, predicate, iterator) - return entities.minByOrNull { it.squaredDistanceTo(pos) } + return closest } /** @@ -62,15 +65,18 @@ object EntityUtils { * * @param pos The position to search from. * @param distance The maximum distance to search for entities. + * @param pointer The mutable list to store the entities in. * @param predicate Optional predicate to filter entities. It allows custom filtering based on entity properties. + * @param iterator Optional iterator to perform operations on each entity. * @return A list of entities of type [T] within the specified distance from the position, excluding the player. * */ inline fun SafeContext.getFastEntities( pos: Vec3d, distance: Double, - pointer: MutableList, + pointer: MutableList? = nullptr, noinline predicate: (T) -> Boolean = { true }, + noinline iterator: (T) -> Unit = { }, ) { val chunks = ceil(distance / 16).toInt() val sectionX = pos.x.toInt() shr 4 @@ -85,6 +91,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(pointer) { entity -> + iterator(entity) entity != player && entity.squaredDistanceTo(pos) <= distance * distance && predicate(entity) } } @@ -98,14 +105,17 @@ object EntityUtils { * 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. + * @param pointer The mutable list to store the entities in. + * @param predicate Optional predicate to filter entities. It allows custom filtering based on entity properties. + * @param iterator Optional iterator to perform operations on each entity. */ inline fun SafeContext.getEntities( - pointer: MutableList, - noinline predicate: (T) -> Boolean = { true } + pointer: MutableList? = nullptr, + noinline predicate: (T) -> Boolean = { true }, + noinline iterator: (T) -> Unit = { }, ) { world.entities.filterIsInstanceTo(pointer) { entity -> + iterator(entity) entity != player && predicate(entity) } } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index e95982c8a..6f140e333 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -11,6 +11,7 @@ accessible field net/minecraft/client/world/ClientWorld entityManager Lnet/minec # Entity accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; accessible method net/minecraft/entity/Entity movementInputToVelocity (Lnet/minecraft/util/math/Vec3d;FF)Lnet/minecraft/util/math/Vec3d; +accessible method net/minecraft/entity/passive/AbstractHorseEntity setHorseFlag (IZ)V # Camera accessible method net/minecraft/client/render/Camera setPos (DDD)V @@ -34,6 +35,12 @@ accessible field net/minecraft/text/Style insertion Ljava/lang/String; accessible field net/minecraft/text/Style font Lnet/minecraft/util/Identifier; accessible method net/minecraft/text/Style (Lnet/minecraft/text/TextColor;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lnet/minecraft/text/ClickEvent;Lnet/minecraft/text/HoverEvent;Ljava/lang/String;Lnet/minecraft/util/Identifier;)V +# Packet +accessible field net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket entityId I +accessible field net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket type Lnet/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler; +accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler +accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractAtHandler + # 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; diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index e73266017..a7cd81fd5 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -22,11 +22,13 @@ "render.GameRendererMixin", "render.GlStateManagerMixin", "render.InGameHudMixin", - "render.VertexBufferMixin", "render.LightmapTextureManagerMixin", "render.LivingEntityRendererMixin", "render.RenderTickCounterMixin", - "render.WorldRendererMixin" + "render.VertexBufferMixin", + "render.WorldRendererMixin", + "world.WorldMixin", + "items.TridentMixin" ], "injectors": { "defaultRequire": 1