diff --git a/common/src/main/java/com/lambda/mixin/ClientConnectionMixin.java b/common/src/main/java/com/lambda/mixin/ClientConnectionMixin.java index 29cc5bb7a..bed77b462 100644 --- a/common/src/main/java/com/lambda/mixin/ClientConnectionMixin.java +++ b/common/src/main/java/com/lambda/mixin/ClientConnectionMixin.java @@ -6,7 +6,9 @@ import io.netty.channel.ChannelHandlerContext; import net.minecraft.network.ClientConnection; import net.minecraft.network.NetworkSide; +import net.minecraft.network.listener.ClientPacketListener; import net.minecraft.network.listener.PacketListener; +import net.minecraft.network.listener.ServerPacketListener; import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.c2s.handshake.ConnectionIntent; import net.minecraft.text.Text; @@ -25,14 +27,18 @@ public class ClientConnectionMixin { @Inject(method = "send(Lnet/minecraft/network/packet/Packet;)V", at = @At("HEAD"), cancellable = true) private void sendingPacket(Packet packet, final CallbackInfo callbackInfo) { - if (EventFlow.post(new PacketEvent.Send.Pre(packet)).isCanceled()) { + if (side != NetworkSide.SERVERBOUND) return; + + if (EventFlow.post(new PacketEvent.Send.Pre((Packet) packet)).isCanceled()) { callbackInfo.cancel(); } } @Inject(method = "send(Lnet/minecraft/network/packet/Packet;)V", at = @At("RETURN")) private void sendingPacketPost(Packet packet, final CallbackInfo callbackInfo) { - EventFlow.post(new PacketEvent.Send.Post(packet)); + if (side != NetworkSide.SERVERBOUND) return; + + EventFlow.post(new PacketEvent.Send.Post((Packet) packet)); } @Inject(method = "channelRead0(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraft/network/packet/Packet;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ClientConnection;handlePacket(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;)V", shift = At.Shift.BEFORE), cancellable = true, require = 1) @@ -42,7 +48,8 @@ private void receivingPacket( CallbackInfo callbackInfo ) { if (side != NetworkSide.CLIENTBOUND) return; - if (EventFlow.post(new PacketEvent.Receive.Pre(packet)).isCanceled()) { + + if (EventFlow.post(new PacketEvent.Receive.Pre((Packet) packet)).isCanceled()) { callbackInfo.cancel(); } } @@ -55,7 +62,7 @@ private void receivingPacketPost( ) { if (side != NetworkSide.CLIENTBOUND) return; - EventFlow.post(new PacketEvent.Receive.Post(packet)); + EventFlow.post(new PacketEvent.Receive.Post((Packet) packet)); } @Inject(method = "connect(Ljava/lang/String;ILnet/minecraft/network/listener/PacketListener;Lnet/minecraft/network/packet/c2s/handshake/ConnectionIntent;)V", at = @At("HEAD")) diff --git a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 3d2e07027..9ffcd5c65 100644 --- a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -6,10 +6,10 @@ import com.lambda.event.events.ScreenEvent; import com.lambda.event.events.ScreenHandlerEvent; import com.lambda.event.events.TickEvent; +import com.lambda.interaction.RotationManager; import com.lambda.module.modules.player.Interact; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider; import net.minecraft.client.network.ClientPlayerInteractionManager; import org.jetbrains.annotations.Nullable; @@ -27,6 +27,7 @@ public class MinecraftClientMixin { @Inject(method = "tick", at = @At("HEAD")) void onTickPre(CallbackInfo ci) { EventFlow.post(new TickEvent.Pre()); + RotationManager.update(); } @Inject(method = "tick", at = @At("RETURN")) 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 03da40258..b04d46a98 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java @@ -1,10 +1,13 @@ package com.lambda.mixin.entity; import com.lambda.event.EventFlow; +import com.lambda.event.events.AttackEvent; import com.lambda.event.events.InteractionEvent; import net.minecraft.client.MinecraftClient; 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.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; @@ -13,6 +16,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.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(ClientPlayerInteractionManager.class) @@ -27,4 +31,14 @@ public void interactBlockHead(final ClientPlayerEntity player, final Hand hand, if (client.world == null) return; EventFlow.post(new InteractionEvent.Block(client.world, hitResult)); } + + @Inject(method = "attackEntity", at = @At("HEAD"), cancellable = true) + void onAttackPre(PlayerEntity player, Entity target, CallbackInfo ci) { + if (EventFlow.post(new AttackEvent.Pre(target)).isCanceled()) ci.cancel(); + } + + @Inject(method = "attackEntity", at = @At("TAIL")) + void onAttackPost(PlayerEntity player, Entity target, CallbackInfo ci) { + EventFlow.post(new AttackEvent.Post(target)); + } } 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 1e1ca65da..89cb14a95 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -3,6 +3,7 @@ import com.lambda.Lambda; import com.lambda.event.EventFlow; import com.lambda.event.events.MovementEvent; +import com.lambda.event.events.TickEvent; import com.lambda.interaction.PlayerPacketManager; import com.lambda.interaction.RotationManager; import net.minecraft.client.input.Input; @@ -55,6 +56,7 @@ void onMove(MovementType movementType, Vec3d movement, CallbackInfo ci) { @Redirect(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick(ZF)V")) void processMovement(Input input, boolean slowDown, float slowDownFactor) { input.tick(slowDown, slowDownFactor); + RotationManager.BaritoneProcessor.processPlayerMovement(input, slowDown, slowDownFactor); EventFlow.post(new MovementEvent.InputUpdate(input, slowDown, slowDownFactor)); } @@ -70,6 +72,16 @@ void sendBegin(CallbackInfo ci) { autoJumpEnabled = Lambda.getMc().options.getAutoJump().getValue(); } + @Inject(method = "tick", at = @At(value = "HEAD")) + void onTickPre(CallbackInfo ci) { + EventFlow.post(new TickEvent.Player.Pre()); + } + + @Inject(method = "tick", at = @At(value = "RETURN")) + void onTickPost(CallbackInfo ci) { + EventFlow.post(new TickEvent.Player.Post()); + } + @Redirect(method = "tickNewAi", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getYaw()F")) float fixHeldItemYaw(ClientPlayerEntity instance) { return Objects.requireNonNullElse(RotationManager.getHandYaw(), instance.getYaw()); 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 8a8dbbc8b..c1f2dc801 100644 --- a/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java @@ -45,6 +45,22 @@ void onJump(CallbackInfo ci) { self.velocityDirty = true; } + @Inject(method = "travel", at = @At("HEAD"), cancellable = true) + void onTravelPre(Vec3d movementInput, CallbackInfo ci) { + LivingEntity self = (LivingEntity) (Object) this; + if (self != Lambda.getMc().player) return; + + if (EventFlow.post(new MovementEvent.Travel.Pre()).isCanceled()) ci.cancel(); + } + + @Inject(method = "travel", at = @At("TAIL")) + void onTravelPost(Vec3d movementInput, CallbackInfo ci) { + LivingEntity self = (LivingEntity) (Object) this; + if (self != Lambda.getMc().player) return; + + EventFlow.post(new MovementEvent.Travel.Post()); + } + @Redirect(method = "travel", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;getPitch()F")) private float hookModifyFallFlyingPitch(LivingEntity entity) { Float pitch = RotationManager.getMovementPitch(); 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 128bec35a..603808b24 100644 --- a/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java @@ -28,4 +28,14 @@ private float injectHeadYaw(PlayerEntity instance) { Float yaw = RotationManager.getRenderYaw(); return (yaw != null) ? yaw : instance.getYaw(); } + + @Redirect(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;getYaw()F")) + private float injectAttackFix(PlayerEntity instance) { + if ((Object) this != MinecraftClient.getInstance().player) { + return instance.getYaw(); + } + + Float yaw = RotationManager.getMovementYaw(); + return (yaw != null) ? yaw : instance.getYaw(); + } } diff --git a/common/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java b/common/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java index 224513f02..eb0e87ae6 100644 --- a/common/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java +++ b/common/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java @@ -2,6 +2,7 @@ import com.lambda.module.modules.movement.Speed; import com.lambda.module.modules.movement.Sprint; +import com.lambda.module.modules.movement.TargetStrafe; import net.minecraft.client.option.KeyBinding; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -19,5 +20,6 @@ void autoSprint(CallbackInfoReturnable cir) { if (Sprint.INSTANCE.isEnabled()) cir.setReturnValue(true); if (Speed.INSTANCE.isEnabled() && Speed.getMode() == Speed.Mode.GRIM_STRAFE) cir.setReturnValue(true); + if (TargetStrafe.INSTANCE.isEnabled() && TargetStrafe.isActive()) cir.setReturnValue(true); } } diff --git a/common/src/main/java/com/lambda/mixin/render/RenderTickCounterMixin.java b/common/src/main/java/com/lambda/mixin/render/RenderTickCounterMixin.java index dd0f62f6e..7b87a3c11 100644 --- a/common/src/main/java/com/lambda/mixin/render/RenderTickCounterMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/RenderTickCounterMixin.java @@ -20,7 +20,7 @@ public class RenderTickCounterMixin { @Inject(method = "beginRenderTick", at = @At("HEAD"), cancellable = true) private void beginRenderTick(long timeMillis, CallbackInfoReturnable ci) { - lastFrameDuration = (timeMillis - prevTimeMillis) / TimerManager.getTickLength(); + lastFrameDuration = (timeMillis - prevTimeMillis) / TimerManager.INSTANCE.getLength(); prevTimeMillis = timeMillis; tickDelta += lastFrameDuration; int i = (int) tickDelta; diff --git a/common/src/main/java/com/lambda/mixin/world/BlockCollisionSpliteratorMixin.java b/common/src/main/java/com/lambda/mixin/world/BlockCollisionSpliteratorMixin.java new file mode 100644 index 000000000..336f9ee14 --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/world/BlockCollisionSpliteratorMixin.java @@ -0,0 +1,24 @@ +package com.lambda.mixin.world; + +import com.google.common.collect.AbstractIterator; +import com.lambda.event.EventFlow; +import com.lambda.event.events.WorldEvent; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockCollisionSpliterator; +import net.minecraft.world.BlockView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(BlockCollisionSpliterator.class) +public abstract class BlockCollisionSpliteratorMixin > { + @Redirect(method = "computeNext", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;getCollisionShape(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/ShapeContext;)Lnet/minecraft/util/shape/VoxelShape;")) + private VoxelShape collisionShapeRedirect(BlockState instance, BlockView blockView, BlockPos blockPos, ShapeContext shapeContext) { + VoxelShape collisionShape = instance.getCollisionShape(blockView, blockPos, shapeContext); + WorldEvent.Collision event = EventFlow.post(new WorldEvent.Collision(blockPos, instance, collisionShape)); + return event.getShape(); + } +} 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 1ddb53ea6..bcaf64897 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -1,14 +1,14 @@ package com.lambda.config.groups import com.lambda.config.Configurable -import com.lambda.util.world.raycast.RayCastMask class InteractionSettings( c: Configurable, + defaultReach: Double = 4.9, vis: () -> Boolean = { true }, ) : InteractionConfig { - override val reach by c.setting("Reach", 4.9, 0.1..10.0, 0.1, "Players reach / range", " blocks", vis) + override val reach by c.setting("Reach", defaultReach, 0.1..10.0, 0.1, "Players reach / range", " blocks", vis) override val useRayCast by c.setting("Raycast", false, "Verify hit vector with ray casting (for very strict ACs)", vis) - override val resolution by c.setting("Resolution", 5, 1..20, 1, "How many raycast checks per surface (will be squared)") { vis() && useRayCast } + override val resolution by c.setting("Resolution", 10, 1..30, 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) } \ 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 8dae291d3..f5e038bb0 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -22,7 +22,7 @@ class RotationSettings( /** * If true, rotation will be instant without any transition. If false, rotation will transition over time. */ - private var instant by c.setting("Instant Rotation", true, "Instantly rotate", vis) + var instant by c.setting("Instant Rotation", true, "Instantly rotate", vis) /** * The mean (average/base) value used to calculate rotation speed. diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt new file mode 100644 index 000000000..aef28e9bf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -0,0 +1,100 @@ +package com.lambda.config.groups + +import com.lambda.config.Configurable +import com.lambda.context.SafeContext +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.threading.runSafe +import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.world.WorldUtils.getFastEntities +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.mob.MobEntity +import net.minecraft.entity.passive.PassiveEntity + +abstract class Targeting( + c: Configurable, + vis: () -> Boolean = { true }, + defaultRange: Double, + maxRange: Double +) : TargetingConfig { + override val targetingRange by c.setting("Targeting Range", defaultRange, 1.0..maxRange, 0.05) { vis() } + + override val players by c.setting("Players", true) { vis() } + private val mobs by c.setting("Mobs", true) { vis() } + private val hostilesSetting by c.setting("Hostiles", true) { vis() && mobs } + private val animalsSetting by c.setting("Animals", true) { vis() && mobs } + override val hostiles get() = mobs && hostilesSetting + override val animals get() = mobs && animalsSetting + + override val invisible by c.setting("Invisible", true) { vis() } + override val dead by c.setting("Dead", false) { vis() } + + fun getEntities(): List = + mutableListOf().apply { + runSafe { + getFastEntities( + player.pos, targetingRange, this@apply, + predicate = { entity -> validate(player, entity) } + ) + } + } + + open fun validate(player: ClientPlayerEntity, entity: LivingEntity) = when { + !players && entity.isPlayer -> false + !animals && entity is PassiveEntity -> false + !hostiles && entity is MobEntity -> false + + !invisible && entity.isInvisibleTo(player) -> false + !dead && entity.isDead -> false + + else -> true + } + + class Combat( + c: Configurable, + vis: () -> Boolean = { true }, + ) : Targeting(c, vis, 5.0, 16.0) { + val fov by c.setting("FOV Limit", 180, 5..180, 1) { vis() } + val priority by c.setting("Priority", Priority.DISTANCE) { vis() } + + override fun validate(player: ClientPlayerEntity, entity: LivingEntity): Boolean { + if (fov < 180 && player.rotation dist player.eyePos.rotationTo(entity.pos) > fov) return false + return super.validate(player, entity) + } + + fun getTarget(): LivingEntity? = runSafe { + var best: LivingEntity? = null + var bestFactor = Double.MAX_VALUE + + val comparator = { entity: LivingEntity, _: Int -> + val factor = priority.factor(this, entity) + if (factor < bestFactor) { + best = entity + bestFactor = factor + } + } + + val predicate = { entity: LivingEntity -> + validate(player, entity) + } + + getFastEntities(player.pos, targetingRange, null, comparator, predicate) + + return@runSafe best + } + } + + class ESP( + c: Configurable, + vis: () -> Boolean = { true }, + ) : Targeting(c, vis, 128.0, 1024.0) + + @Suppress("Unused") + enum class Priority(val factor: SafeContext.(LivingEntity) -> Double) { + DISTANCE({ player.pos distSq it.pos }), + HEALTH({ it.health.toDouble() }), + FOV({ player.rotation dist player.eyePos.rotationTo(it.pos) }) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/TargetingConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/TargetingConfig.kt new file mode 100644 index 000000000..731e5e0a2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/TargetingConfig.kt @@ -0,0 +1,12 @@ +package com.lambda.config.groups + +interface TargetingConfig { + val targetingRange: Double + + val players: Boolean + val hostiles: Boolean + val animals: Boolean + + val invisible: Boolean + val dead: Boolean +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/CustomModuleWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/CustomModuleWindowSerializer.kt index d394df2ea..cf2c315f9 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/gui/CustomModuleWindowSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/CustomModuleWindowSerializer.kt @@ -1,6 +1,7 @@ package com.lambda.config.serializer.gui import com.google.gson.* +import com.lambda.gui.api.component.core.DockingRect import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow import com.lambda.module.ModuleRegistry @@ -24,8 +25,12 @@ object CustomModuleWindowSerializer : JsonSerializer, JsonDe addProperty("height", it.height) addProperty("isOpen", it.isOpen) add("position", JsonArray().apply { - add(it.position.x) - add(it.position.y) + add(it.serializedPosition.x) + add(it.serializedPosition.y) + }) + add("docking", JsonArray().apply { + add(it.dockingH.ordinal) + add(it.dockingV.ordinal) }) } } ?: JsonNull.INSTANCE @@ -47,10 +52,12 @@ object CustomModuleWindowSerializer : JsonSerializer, JsonDe width = it["width"].asDouble height = it["height"].asDouble isOpen = it["isOpen"].asBoolean - position = Vec2d( + serializedPosition = Vec2d( it["position"].asJsonArray[0].asDouble, it["position"].asJsonArray[1].asDouble ) + dockingH = DockingRect.HAlign.entries[it["docking"].asJsonArray[0].asInt] + dockingV = DockingRect.VAlign.entries[it["docking"].asJsonArray[1].asInt] } } ?: throw JsonParseException("Invalid window data") } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt index 45b288cad..107c81694 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt @@ -1,6 +1,7 @@ package com.lambda.config.serializer.gui import com.google.gson.* +import com.lambda.gui.api.component.core.DockingRect import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.gui.impl.clickgui.windows.tag.TagWindow import com.lambda.gui.impl.hudgui.LambdaHudGui @@ -20,9 +21,14 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer, JsonDeserializer LambdaClickGui - in ModuleTag.hudDefaults -> LambdaHudGui + val gui = when (it["group"].asString) { + "main"-> LambdaClickGui + "hud" -> LambdaHudGui else -> return@let null } @@ -43,10 +49,12 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer { - ClientEvent.Timer(1.0).post { - tickLength = 50f / speed.toFloat() - } + fun getLength(): Float { + var length = 50f + + ClientEvent.Timer(1.0).post { + length /= speed.toFloat() } + + lastTickLength = length + return length } } diff --git a/common/src/main/kotlin/com/lambda/event/events/AttackEvent.kt b/common/src/main/kotlin/com/lambda/event/events/AttackEvent.kt new file mode 100644 index 000000000..c72f93b07 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/event/events/AttackEvent.kt @@ -0,0 +1,11 @@ +package com.lambda.event.events + +import com.lambda.event.Event +import com.lambda.event.callback.Cancellable +import com.lambda.event.callback.ICancellable +import net.minecraft.entity.Entity + +abstract class AttackEvent(val entity: Entity) : Event { + class Pre(entity: Entity) : AttackEvent(entity), ICancellable by Cancellable() + class Post(entity: Entity) : AttackEvent(entity) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/MovementEvent.kt b/common/src/main/kotlin/com/lambda/event/events/MovementEvent.kt index 543074557..4087c26eb 100644 --- a/common/src/main/kotlin/com/lambda/event/events/MovementEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/MovementEvent.kt @@ -9,6 +9,11 @@ abstract class MovementEvent : Event { class Pre : MovementEvent() class Post : MovementEvent() + abstract class Travel : MovementEvent() { + class Pre : MovementEvent(), ICancellable by Cancellable() + class Post : MovementEvent() + } + class InputUpdate( val input: Input, var slowDown: Boolean, diff --git a/common/src/main/kotlin/com/lambda/event/events/PacketEvent.kt b/common/src/main/kotlin/com/lambda/event/events/PacketEvent.kt index af8ada9fc..7a9156882 100644 --- a/common/src/main/kotlin/com/lambda/event/events/PacketEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/PacketEvent.kt @@ -6,13 +6,17 @@ import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable import com.lambda.event.events.PacketEvent.Receive import com.lambda.event.events.PacketEvent.Send +import com.lambda.util.ClientPacket +import com.lambda.util.ServerPacket +import net.minecraft.network.listener.ClientPacketListener +import net.minecraft.network.listener.ServerPacketListener import net.minecraft.network.packet.Packet /** * An abstract class representing a [PacketEvent] in the [EventFlow]. * * A [PacketEvent] is a type of [Event] that is triggered when a packet is sent or received. - * It has two subclasses: [Send] and [Receive], + * It has two sealed subclasses: [Send] and [Receive], * which are triggered when a packet is sent and received, respectively. * * Each subclass has two further subclasses: `Pre` and `Post`, @@ -25,20 +29,42 @@ import net.minecraft.network.packet.Packet */ abstract class PacketEvent : Event { /** - * Representing a [PacketEvent] that is triggered when a packet is sent. + * Represents a [PacketEvent] that is triggered when a packet is sent. * It has two subclasses: [Pre] and [Post], which are triggered before and after the packet is sent. */ - abstract class Send : PacketEvent() { - class Pre(val packet: Packet<*>) : Send(), ICancellable by Cancellable() - class Post(val packet: Packet<*>) : Send() + sealed class Send : PacketEvent() { + /** + * Represents the event triggered before a packet is sent. + * + * @param packet the packet that is about to be sent. + */ + class Pre(val packet: ClientPacket) : Send(), ICancellable by Cancellable() + + /** + * Represents the event triggered after a packet is sent. + * + * @param packet the packet that has been sent. + */ + class Post(val packet: ClientPacket) : Send() } /** - * Representing a `PacketEvent` that is triggered when a packet is received. + * Represents a [PacketEvent] that is triggered when a packet is received. * It has two subclasses: [Pre] and [Post], which are triggered before and after the packet is received. */ - abstract class Receive : PacketEvent() { - class Pre(val packet: Packet<*>) : Receive(), ICancellable by Cancellable() - class Post(val packet: Packet<*>) : Receive() + sealed class Receive : PacketEvent() { + /** + * Represents the event triggered before a packet is received. + * + * @param packet the packet that is about to be received. + */ + class Pre(val packet: ServerPacket) : Receive(), ICancellable by Cancellable() + + /** + * Represents the event triggered after a packet is received. + * + * @param packet the packet that has been received. + */ + class Post(val packet: ServerPacket) : Receive() } } diff --git a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt index e247e328b..afe989011 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -20,9 +20,8 @@ abstract class RenderEvent : Event { } abstract class GUI(val scale: Double) : RenderEvent() { - class HUD(scaleFactor: Double) : GUI(scaleFactor) - class Scaled(scaleFactor: Double) : GUI(scaleFactor) + class HUD(scaleFactor: Double) : GUI(scaleFactor) class Fixed : GUI(1.0) val screenSize = Vec2d(mc.window.framebufferWidth, mc.window.framebufferHeight) / scale 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 bfc1b547c..95f330600 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -24,7 +24,7 @@ abstract class RotationEvent : Event { * @property strafeYaw The angle at which the player will move when pressing W * Changing this value will never force the anti cheat to flag you because RotationManager is designed to modify the key input instead */ - class Strafe(var strafeYaw: Double, val input: Input) : RotationEvent() + class StrafeInput(var strafeYaw: Double, val input: Input) : RotationEvent() class Post(val context: RotationContext) : RotationEvent() } diff --git a/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt b/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt index 9e310ac0b..85f9b1a0a 100644 --- a/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/TickEvent.kt @@ -26,4 +26,19 @@ abstract class TickEvent : Event { * A class representing a [TickEvent] that is triggered after each tick of the game loop. */ class Post : TickEvent() -} \ No newline at end of file + + /** + * A class representing a [TickEvent] that is triggered when the player gets ticked. + */ + abstract class Player : TickEvent() { + /** + * A class representing a [TickEvent.Player] that is triggered before each player tick. + */ + class Pre : Player() + + /** + * A class representing a [TickEvent.Player] that is triggered after each player tick. + */ + class Post : Player() + } +} 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 ffb715aed..f1fab7628 100644 --- a/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt @@ -7,6 +7,7 @@ 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.shape.VoxelShape import net.minecraft.world.chunk.WorldChunk abstract class WorldEvent : Event { @@ -34,4 +35,6 @@ abstract class WorldEvent : Event { class EntitySpawn( val entity: Entity ) : WorldEvent(), ICancellable by Cancellable() + + class Collision(val pos: BlockPos, val state: BlockState, var shape: VoxelShape) : WorldEvent() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt index 4cd9b0cbc..4b0c653e6 100644 --- a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt +++ b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt @@ -7,6 +7,7 @@ import com.lambda.event.Muteable import com.lambda.task.Task import com.lambda.threading.runConcurrent import com.lambda.threading.runSafe +import com.lambda.util.selfReference /** @@ -87,6 +88,57 @@ class SafeListener( return listener } + /** + * This function registers a new [SafeListener] for a generic [Event] type [T]. + * The [function] is executed on the same thread where the [Event] was dispatched. + * The [function] will only be executed when the context satisfies certain safety conditions. + * These conditions are met when none of the following [SafeContext] properties are null: + * - [SafeContext.world] + * - [SafeContext.player] + * - [SafeContext.interaction] + * - [SafeContext.connection] + * + * This typically occurs when the user is in-game. + * + * After the [function] is executed once, the [SafeListener] will be automatically unsubscribed. + * + * Usage: + * ```kotlin + * private val event by listenOnce { event -> + * player.sendMessage("Event received only once: $event") + * // event is stored in the value + * // event is unsubscribed after execution + * } + * ``` + * + * @param T The type of the event to listen for. This should be a subclass of Event. + * @param priority The priority of the listener. Listeners with higher priority will be executed first. The Default value is 0. + * @param alwaysListen If true, the listener will be executed even if it is muted. The Default value is false. + * @param function The function to be executed when the event is posted. This function should take a SafeContext and an event of type T as parameters. + * @return The newly created and registered [SafeListener]. + */ + inline fun Any.listenOnce( + priority: Int = 0, + alwaysListen: Boolean = false, + noinline function: SafeContext.(T) -> Unit = {}, + ): Lazy { + // This doesn't leak memory because the owner still has a reference to the listener + var value: T? = null + + val destroyable by selfReference { + SafeListener(priority, this@listenOnce, alwaysListen) { event -> + function(event as T) + value = event + + EventFlow.syncListeners.unsubscribe(self) + } + } + + EventFlow.syncListeners.subscribe(destroyable) + + return lazy { value } + } + /** * Registers a new [SafeListener] for a generic [Event] type [T] within the context of a [Task]. * The [function] is executed on the same thread where the [Event] was dispatched. diff --git a/common/src/main/kotlin/com/lambda/event/listener/UnsafeListener.kt b/common/src/main/kotlin/com/lambda/event/listener/UnsafeListener.kt index 26147cd9d..be8a7d997 100644 --- a/common/src/main/kotlin/com/lambda/event/listener/UnsafeListener.kt +++ b/common/src/main/kotlin/com/lambda/event/listener/UnsafeListener.kt @@ -5,7 +5,9 @@ import com.lambda.event.Event import com.lambda.event.EventFlow import com.lambda.event.Muteable import com.lambda.event.listener.SafeListener.Companion.concurrentListener +import com.lambda.event.listener.SafeListener.Companion.listenOnce import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.util.selfReference /** * An [UnsafeListener] is a specialized type of [Listener] that operates without a [SafeContext]. @@ -79,6 +81,54 @@ class UnsafeListener( return listener } + /** + * Registers a new [UnsafeListener] for a generic [Event] type [T]. + * The [function] is executed only once when the [Event] is dispatched. + * This function should only be used when the [function] performs read actions on the game data. + * For only in-game related contexts, use the [SafeListener.listenOnce] function instead. + * The listener will be automatically unsubscribed after the first execution. + * This function is useful for one-time event handling. + * + * Usage: + * ```kotlin + * private val event by unsafeListenOnce { event -> + * println("Unsafe event received only once: $event") + * // no safe access to player or world + * // event is stored in the value + * // event is unsubscribed after execution + * } + * ``` + * + * After the [function] is executed once, the [SafeListener] will be automatically unsubscribed. + * + * @param T The type of the event to listen for. This should be a subclass of Event. + * @param priority The priority of the listener. Listeners with higher priority will be executed first. The Default value is 0. + * @param alwaysListen If true, the listener will be executed even if it is muted. The Default value is false. + * @param function The function to be executed when the event is posted. This function should take an event of type T as a parameter. + * @return The newly created and registered [UnsafeListener]. + */ + inline fun Any.unsafeListenOnce( + priority: Int = 0, + alwaysListen: Boolean = false, + noinline function: (T) -> Unit, + ): Lazy { + // This doesn't leak memory because the owner still has a reference to the listener + var value: T? = null + + val destroyable by selfReference { + UnsafeListener(priority, this@unsafeListenOnce, alwaysListen) { event -> + function(event as T) + value = event + + EventFlow.syncListeners.unsubscribe(self) + } + } + + EventFlow.syncListeners.subscribe(destroyable) + + return lazy { value } + } + /** * Registers a new [UnsafeListener] for a generic [Event] type [T]. * The [function] is executed on a new thread running asynchronously to the game thread. diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 53216f32d..fdf63e1f0 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -3,12 +3,18 @@ package com.lambda.graphics import com.lambda.Lambda.mc import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.graphics.animation.AnimationTicker +import com.lambda.graphics.buffer.FrameBuffer import com.lambda.graphics.gl.GlStateUtils.setupGL import com.lambda.graphics.gl.Matrices -import com.lambda.graphics.gl.Matrices.resetMatrix -import com.lambda.graphics.gl.Matrices.translate +import com.lambda.graphics.gl.Matrices.resetMatrices import com.lambda.graphics.renderer.esp.global.StaticESP import com.lambda.graphics.renderer.esp.global.DynamicESP +import com.lambda.graphics.shader.Shader +import com.lambda.module.modules.client.ClickGui import com.lambda.module.modules.client.GuiSettings import com.lambda.util.math.Vec2d import com.mojang.blaze3d.systems.RenderSystem.getProjectionMatrix @@ -17,27 +23,39 @@ import org.joml.Matrix4f object RenderMain { val projectionMatrix = Matrix4f() val modelViewMatrix: Matrix4f get() = Matrices.peek() - var screenSize = Vec2d.ZERO + private val hudAnimation0 = with(AnimationTicker()) { + listener { + tick() + } + + exp(0.0, 1.0, { + if (mc.currentScreen == null) ClickGui.closeSpeed else ClickGui.openSpeed + }) { mc.currentScreen == null } + } + + private val frameBuffer = FrameBuffer() + private val shader = Shader("post/cgui_animation", "renderer/pos_tex") + private val hudAnimation by hudAnimation0 + @JvmStatic fun render2D() { - resetMatrix() - translate(0.0, 0.0, -3000.0) + resetMatrices(Matrix4f().translate(0f, 0f, -3000f)) setupGL { rescale(1.0) RenderEvent.GUI.Fixed().post() rescale(GuiSettings.scale) - RenderEvent.GUI.HUD(GuiSettings.scale).post() + drawHUD() RenderEvent.GUI.Scaled(GuiSettings.scale).post() } } @JvmStatic fun render3D(matrix: Matrix4f) { - resetMatrix(matrix) + resetMatrices(matrix) projectionMatrix.set(getProjectionMatrix()) setupGL { @@ -57,4 +75,19 @@ object RenderMain { screenSize = Vec2d(scaledWidth, scaledHeight) projectionMatrix.setOrtho(0f, scaledWidth.toFloat(), scaledHeight.toFloat(), 0f, 1000f, 21000f) } + + private fun drawHUD() { + if (hudAnimation < 0.001) return + + if (hudAnimation > 0.999) { + RenderEvent.GUI.HUD(GuiSettings.scale).post() + return + } + + frameBuffer.write { + RenderEvent.GUI.HUD(GuiSettings.scale).post() + }.read(shader) { + it["u_Progress"] = hudAnimation + } + } } diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt index cf849d16b..df8765038 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt @@ -5,6 +5,10 @@ import java.awt.Color interface IRenderContext { fun vec3(x: Double, y: Double, z: Double): IRenderContext fun vec2(x: Double, y: Double): IRenderContext + + fun vec3m(x: Double, y: Double, z: Double): IRenderContext + fun vec2m(x: Double, y: Double): IRenderContext + fun float(v: Double): IRenderContext fun color(color: Color): IRenderContext fun end(): Int diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index af6050689..b6cb584cc 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -3,6 +3,7 @@ package com.lambda.graphics.buffer.vao import com.lambda.graphics.buffer.vao.vertex.BufferUsage import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Memory.address import com.lambda.graphics.gl.Memory.byteBuffer import com.lambda.graphics.gl.Memory.capacity @@ -21,6 +22,7 @@ import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer import com.lambda.graphics.gl.VaoUtils.unbindVertexArray import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer import com.lambda.threading.runGameScheduled +import org.joml.* import org.lwjgl.opengl.GL30C.* import java.awt.Color import java.nio.ByteBuffer @@ -88,6 +90,23 @@ class VAO( return this } + override fun vec3m(x: Double, y: Double, z: Double): IRenderContext { + Matrices.vertexTransformer?.let { mat -> + val vec = Vector4d(x, y, z, 1.0).apply(mat::transform) + vec3(vec.x, vec.y, vec.z) + } ?: vec3(x, y, z) + + return this + } + + override fun vec2m(x: Double, y: Double): IRenderContext { + Matrices.vertexTransformer?.let { mat -> + val vec = Vector4d(x, y, 0.0, 1.0).apply(mat::transform) + vec2(vec.x, vec.y) + } ?: vec2(x, y) + return this + } + override fun float(v: Double): VAO { verticesPosition += float(verticesPosition, v) return this diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt index 752e12293..399416eac 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt @@ -16,13 +16,15 @@ enum class VertexAttrib(val componentCount: Int, componentSize: Int, val normali POS_UV(Vec2, Vec2), // GUI - FONT(Vec2, Vec2, Color), // pos, uv, color + FONT(Vec3, Vec2, Color), // pos, uv, color RECT_FILLED(Vec2, Vec2, Vec2, Float, Float, Color), // pos, uv, size, roundRadius, shade, color RECT_OUTLINE(Vec2, Float, Float, Color), // pos, alpha, shade, color // WORLD DYNAMIC_RENDERER(Vec3, Vec3, Color), // prev pos, pos, color - STATIC_RENDERER(Vec3, Color); // pos, color + STATIC_RENDERER(Vec3, Color), // pos, color + + PARTICLE(Vec3, Vec2, Color); // pos, uv, color val stride = attributes.sumOf { attribute -> attribute.size } } diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt index 245d9dc35..0cf5748c2 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt @@ -1,11 +1,15 @@ package com.lambda.graphics.gl -import org.joml.Matrix4f -import org.joml.Quaternionf +import com.lambda.Lambda.mc +import net.minecraft.util.math.RotationAxis +import net.minecraft.util.math.Vec3d +import org.joml.* object Matrices { private val stack = ArrayDeque(1) + var vertexTransformer: Matrix4d? = null + fun translate(x: Double, y: Double, z: Double) { translate(x.toFloat(), y.toFloat(), z.toFloat()) } @@ -47,8 +51,32 @@ object Matrices { fun peek() = stack.last() - fun resetMatrix(entry: Matrix4f = Matrix4f()) { + fun resetMatrices(entry: Matrix4f) { stack.clear() stack.add(entry) } + + fun withVertexTransform(matrix: Matrix4f, block: () -> Unit) { + vertexTransformer = Matrix4d(matrix) + block() + vertexTransformer = null + } + + fun buildWorldProjection(pos: Vec3d, scale: Double = 1.0, mode: ProjRotationMode = ProjRotationMode.TO_CAMERA) = Matrix4f().apply { + val s = 0.025f * scale.toFloat() + + val rotation = when(mode) { + ProjRotationMode.TO_CAMERA -> mc.gameRenderer.camera.rotation + ProjRotationMode.UP -> RotationAxis.POSITIVE_X.rotationDegrees(90f) + } + + translate(pos.x.toFloat(), pos.y.toFloat(), pos.z.toFloat()) + rotate(rotation) + scale(-s, -s, s) + } + + enum class ProjRotationMode { + TO_CAMERA, + UP + } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DynamicAABB.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DynamicAABB.kt index 35dd9d6ec..852f78901 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DynamicAABB.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DynamicAABB.kt @@ -12,13 +12,11 @@ class DynamicAABB { private var prev: Box? = null private var curr: Box? = null - fun update(box: Box) { - prev = curr + fun update(box: Box): DynamicAABB { + prev = curr ?: box curr = box - if (prev == null) { - prev = box - } + return this } fun reset() { @@ -38,13 +36,8 @@ class DynamicAABB { companion object { val Entity.dynamicBox get() = DynamicAABB().apply { - val box = boundingBox - - val delta = prevPos - pos - val prevBox = Box(box.min + delta, box.max + delta) - - update(prevBox) - update(box) + update(boundingBox.offset(prevPos - pos)) + update(boundingBox) } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt index 79733d92f..454d624b9 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt @@ -7,7 +7,7 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.buffer.vao.vertex.BufferUsage import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer -object StaticESP : StaticESPRenderer(BufferUsage.DYNAMIC) { +object StaticESP : StaticESPRenderer(BufferUsage.DYNAMIC, false) { init { listener { clear() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt index 0817f17e8..26c7c9140 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt @@ -6,7 +6,8 @@ import java.awt.Color import java.util.concurrent.ConcurrentHashMap open class StaticESPRenderer( - usage: BufferUsage = BufferUsage.STATIC + usage: BufferUsage = BufferUsage.STATIC, + private val useVertexCaching: Boolean = true, ) : ESPRenderer(usage, false) { val faceVertices = ConcurrentHashMap() val outlineVertices = ConcurrentHashMap() @@ -37,9 +38,10 @@ open class StaticESPRenderer( x: Double, y: Double, z: Double, color: Color ) = lazy { - storage.getOrPut(Vertex(x, y, z, color)) { - vec3(x, y, z).color(color).end() - } + val vtx = { vec3(x, y, z).color(color).end() } + if (!useVertexCaching) return@lazy vtx() + + storage.getOrPut(Vertex(x, y, z, color), vtx) } data class Vertex(val x: Double, val y: Double, val z: Double, val color: Color) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt index f4caab953..fa1c65ad1 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt @@ -23,14 +23,14 @@ object TextureRenderer { drawInternal(rect) } - fun drawTextureShaded(texture: Texture, rect: Rect, shadeWidthScale: Double = 1.0) { + fun drawTextureShaded(texture: Texture, rect: Rect) { texture.bind() shaderColored.use() shaderColored["u_Time"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0 shaderColored["u_Color1"] = GuiSettings.shadeColor1 shaderColored["u_Color2"] = GuiSettings.shadeColor2 - shaderColored["u_Size"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) / shadeWidthScale + shaderColored["u_Size"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) drawInternal(rect) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 96411fac9..1155946ad 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -18,7 +18,7 @@ class FontRenderer( ) { private val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) - private val scaleMultiplier = 1.0 + var scaleMultiplier = 1.0 /** * Builds the vertex array for rendering the text. @@ -33,10 +33,10 @@ class FontRenderer( iterateText(text, scale, shadow, color) { char, pos1, pos2, color -> grow(4) putQuad( - vec2(pos1.x + position.x, pos1.y + position.y).vec2(char.uv1.x, char.uv1.y).color(color).end(), - vec2(pos1.x + position.x, pos2.y + position.y).vec2(char.uv1.x, char.uv2.y).color(color).end(), - vec2(pos2.x + position.x, pos2.y + position.y).vec2(char.uv2.x, char.uv2.y).color(color).end(), - vec2(pos2.x + position.x, pos1.y + position.y).vec2(char.uv2.x, char.uv1.y).color(color).end() + vec3m(pos1.x + position.x, pos1.y + position.y, 0.0).vec2(char.uv1.x, char.uv1.y).color(color).end(), + vec3m(pos1.x + position.x, pos2.y + position.y, 0.0).vec2(char.uv1.x, char.uv2.y).color(color).end(), + vec3m(pos2.x + position.x, pos2.y + position.y, 0.0).vec2(char.uv2.x, char.uv2.y).color(color).end(), + vec3m(pos2.x + position.x, pos1.y + position.y, 0.0).vec2(char.uv2.x, char.uv1.y).color(color).end() ) } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index bc3efca5f..d8f92e6e6 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -35,6 +35,7 @@ class EmojiGlyphs(zipUrl: String) { LOG.info("Loaded ${emojiMap.size} emojis in $time ms") }.onFailure { LOG.error("Failed to load emojis: ${it.message}", it) + fontTexture = MipmapTexture(BufferedImage(1024, 1024, BufferedImage.TYPE_INT_ARGB)) } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt index 8637e560e..c19f128b1 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt @@ -41,7 +41,7 @@ class FilledRectRenderer : AbstractRectRenderer( val halfSize = size * 0.5 val maxRadius = min(halfSize.x, halfSize.y) - val round = min(roundRadius, maxRadius) + val round = roundRadius.coerceAtMost(maxRadius).coerceAtLeast(0.0) val p1 = pos1 - 0.25 val p2 = pos2 + 0.25 @@ -50,10 +50,10 @@ class FilledRectRenderer : AbstractRectRenderer( grow(4) putQuad( - vec2(p1.x, p1.y).vec2(0.0, 0.0).vec2(size.x, size.y).float(round).float(s).color(leftTop).end(), - vec2(p1.x, p2.y).vec2(0.0, 1.0).vec2(size.x, size.y).float(round).float(s).color(leftBottom).end(), - vec2(p2.x, p2.y).vec2(1.0, 1.0).vec2(size.x, size.y).float(round).float(s).color(rightBottom).end(), - vec2(p2.x, p1.y).vec2(1.0, 0.0).vec2(size.x, size.y).float(round).float(s).color(rightTop).end() + vec2m(p1.x, p1.y).vec2(0.0, 0.0).vec2(size.x, size.y).float(round).float(s).color(leftTop).end(), + vec2m(p1.x, p2.y).vec2(0.0, 1.0).vec2(size.x, size.y).float(round).float(s).color(leftBottom).end(), + vec2m(p2.x, p2.y).vec2(1.0, 1.0).vec2(size.x, size.y).float(round).float(s).color(rightBottom).end(), + vec2m(p2.x, p1.y).vec2(1.0, 0.0).vec2(size.x, size.y).float(round).float(s).color(rightTop).end() ) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt index 8e584c61c..84aa4ed88 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt @@ -47,7 +47,7 @@ class OutlineRectRenderer : AbstractRectRenderer( val halfSize = r.size * 0.5 val maxRadius = min(halfSize.x, halfSize.y) - 0.5 - val round = (roundRadius + size).coerceAtMost(maxRadius) + val round = (roundRadius + size).coerceAtMost(maxRadius).coerceAtLeast(0.0) fun MutableList.buildCorners(base: Vec2d, c: Color, angleRange: IntRange) = repeat(quality) { val min = angleRange.first.toDouble() @@ -57,7 +57,7 @@ class OutlineRectRenderer : AbstractRectRenderer( val pos = base + Vec2d(cos(angle), -sin(angle)) * round val s = shade.toInt().toDouble() - add(vec2(pos.x, pos.y).float(a).float(s).color(c).end()) + add(vec2m(pos.x, pos.y).float(a).float(s).color(c).end()) } val rt = r.rightTop + Vec2d(-round, round) diff --git a/common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt index 1b5d1e96d..d7ff5db88 100644 --- a/common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt @@ -4,7 +4,6 @@ import com.lambda.config.Configurable import com.lambda.config.configurations.GuiConfig import com.lambda.core.Loadable import com.lambda.gui.impl.AbstractClickGui -import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow import com.lambda.gui.impl.clickgui.windows.tag.TagWindow import com.lambda.module.tag.ModuleTag import com.lambda.util.math.Vec2d @@ -15,7 +14,6 @@ abstract class AbstractGuiConfigurable( override val name: String ) : Configurable(GuiConfig), Loadable { var mainWindows by setting("windows", defaultWindows) - open var customWindows = mutableListOf() private val defaultWindows get() = diff --git a/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt index 81c09a471..eff9e86d1 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt @@ -7,5 +7,5 @@ import com.lambda.module.tag.ModuleTag object GuiConfigurable : AbstractGuiConfigurable( LambdaClickGui, ModuleTag.defaults, "gui" ) { - override var customWindows by setting("custom windows", listOf()) + var customWindows by setting("custom windows", listOf()) } diff --git a/common/src/main/kotlin/com/lambda/gui/api/GuiEvent.kt b/common/src/main/kotlin/com/lambda/gui/api/GuiEvent.kt index a9daccdac..abaeddd69 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/GuiEvent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/GuiEvent.kt @@ -4,6 +4,7 @@ import com.lambda.event.Event import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.math.Vec2d +import net.minecraft.world.gen.feature.DeltaFeature abstract class GuiEvent : Event { class Show : GuiEvent() @@ -14,4 +15,5 @@ abstract class GuiEvent : Event { class CharTyped(val char: Char) : GuiEvent() class MouseClick(val button: Mouse.Button, val action: Mouse.Action, val mouse: Vec2d) : GuiEvent() class MouseMove(val mouse: Vec2d) : GuiEvent() + class MouseScroll(val mouse: Vec2d, val delta: Double) : GuiEvent() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt index c8dad409e..d495a43e9 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -24,7 +24,7 @@ abstract class LambdaGui( override val name: String, private val owner: Module? = null ) : Screen(Text.of(name)), IComponent, Nameable, Muteable { - protected var screenSize = Vec2d.ZERO + var screenSize = Vec2d.ZERO override val rect get() = Rect(Vec2d.ZERO, screenSize) val isOpen get() = mc.currentScreen == this @@ -131,6 +131,16 @@ abstract class LambdaGui( onEvent(GuiEvent.MouseMove(rescaleMouse(mouseX, mouseY))) } + override fun mouseScrolled( + mouseX: Double, + mouseY: Double, + horizontalAmount: Double, + verticalAmount: Double + ): Boolean { + onEvent(GuiEvent.MouseScroll(rescaleMouse(mouseX, mouseY), verticalAmount)) + return true + } + final override fun shouldPause() = false private fun rescaleMouse(mouseX: Double, mouseY: Double): Vec2d { diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt b/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt index 3128a9190..0329f3dcf 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt @@ -3,16 +3,39 @@ package com.lambda.gui.api.component import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.button.ListButton import com.lambda.gui.impl.AbstractClickGui +import com.lambda.module.modules.client.ClickGui abstract class ListWindow( owner: AbstractClickGui, ) : WindowComponent(owner) { + private var scrollOffset: Double = 0.0 + private var rubberbandRequest = 0.0 + private var rubberbandDelta = 0.0 + override fun onEvent(e: GuiEvent) { - if (e is GuiEvent.Tick) { - var y = 0.0 - contentComponents.children.forEach { button -> - button.heightOffset = y - y += button.size.y + button.listStep + when (e) { + is GuiEvent.Tick -> { + rubberbandDelta += rubberbandRequest + rubberbandRequest = 0.0 + + rubberbandDelta *= 0.5 + if (rubberbandDelta < 0.05) rubberbandDelta = 0.0 + + var y = scrollOffset + rubberbandDelta + contentComponents.children.forEach { button -> + button.heightOffset = y + y += button.size.y + button.listStep + } + } + is GuiEvent.MouseScroll -> { + val delta = e.delta * 10.0 * ClickGui.scrollSpeed + scrollOffset += delta + + val prevOffset = scrollOffset + val range = -contentComponents.children.sumOf { it.size.y + it.listStep } + scrollOffset = scrollOffset.coerceAtLeast(range).coerceAtMost(0.0) + + rubberbandRequest += prevOffset - scrollOffset } } diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index 3fd197816..7cf4c0044 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -4,6 +4,7 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.graphics.gl.Scissor.scissor import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.RenderLayer +import com.lambda.gui.api.component.core.DockingRect import com.lambda.gui.api.component.core.list.ChildComponent import com.lambda.gui.api.component.core.list.ChildLayer import com.lambda.gui.impl.AbstractClickGui @@ -28,18 +29,33 @@ abstract class WindowComponent( abstract var width: Double abstract var height: Double - var position = Vec2d.ZERO - var isOpen = true override val isActive get() = isOpen private var dragOffset: Vec2d? = null private val padding get() = ClickGui.windowPadding - final override val rect get() = Rect.basedOn(position, width, renderHeightAnimation + titleBarHeight) + private val rectHandler = object : DockingRect() { + override var relativePos = Vec2d.ZERO + override val width get() = this@WindowComponent.width + override val height get() = renderHeightAnimation + titleBarHeight + + override val dockingBase get() = titleBar.center + + override var allowHAlign = ClickGui.allowHAlign + override var allowVAlign = ClickGui.allowVAlign + } + + var serializedPosition by rectHandler::relativePos + var position by rectHandler::position + final override val rect by rectHandler::rect + + var dockingH by rectHandler::dockingH + var dockingV by rectHandler::dockingV + private val contentRect get() = rect.shrink(padding).moveFirst(Vec2d(0.0, titleBarHeight - padding)) - private val titleBar get() = Rect.basedOn(rect.leftTop, rect.size.x, titleBarHeight) + private val titleBar: Rect get() = Rect.basedOn(rect.leftTop, rect.size.x, titleBarHeight) private val titleBarHeight get() = ClickGui.buttonHeight * 1.25 private val renderer = RenderLayer() @@ -65,6 +81,8 @@ abstract class WindowComponent( } is GuiEvent.Render -> { + updateRect() + // TODO: fix blur // BlurPostProcessor.render(rect, ClickGui.windowBlur, guiAnimation) @@ -105,8 +123,14 @@ abstract class WindowComponent( } is GuiEvent.MouseMove -> { + val prevPos = position + dragOffset?.let { position = e.mouse - it + + if (prevPos != position) { + rectHandler.autoDocking() + } } } @@ -133,6 +157,24 @@ abstract class WindowComponent( contentComponents.onEvent(e) } + private fun updateRect() = rectHandler.apply { + screenSize = gui.screenSize + + var updateDocking = false + + if (allowHAlign != ClickGui.allowHAlign) { + allowHAlign = ClickGui.allowHAlign + updateDocking = true + } + + if (allowVAlign != ClickGui.allowVAlign) { + allowVAlign = ClickGui.allowVAlign + updateDocking = true + } + + if (updateDocking) autoDocking() + } + fun focus() { // move window into foreground gui.apply { diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/button/InputBarOverlay.kt b/common/src/main/kotlin/com/lambda/gui/api/component/button/InputBarOverlay.kt index 94024f164..1cc6a8509 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/button/InputBarOverlay.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/button/InputBarOverlay.kt @@ -123,6 +123,6 @@ abstract class InputBarOverlay (val renderer: RenderLayer, owner: ChildLayer.Dra fun toggle() { isActive = !isActive - if (isActive) typed = getText() + if (isActive) typed = getText().filter { isCharAllowed("", it) } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/DockingRect.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/DockingRect.kt new file mode 100644 index 000000000..87c9a0cac --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/DockingRect.kt @@ -0,0 +1,83 @@ +package com.lambda.gui.api.component.core + +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.math.MathUtils.coerceIn +import com.lambda.util.math.MathUtils.roundToStep +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d + +abstract class DockingRect { + abstract var relativePos: Vec2d + protected abstract val width: Double + protected abstract val height: Double + protected val size get() = Vec2d(width, height) + + val rect get() = Rect.basedOn(position, size) + open val dockingBase get() = rect.center + + open val autoDocking = false + open val allowHAlign = true + open val allowVAlign = true + + open var dockingH = HAlign.LEFT; set(to) { + val from = field + field = to + + val delta = to.multiplier - from.multiplier + relativePos += Vec2d.RIGHT * delta * (size.x - screenSize.x) + } + + open var dockingV = VAlign.TOP; set(to) { + val from = field + field = to + + val delta = to.multiplier - from.multiplier + relativePos += Vec2d.BOTTOM * delta * (size.y - screenSize.y) + } + + var screenSize: Vec2d = Vec2d.ZERO + + var position: Vec2d + get() = relativeToAbs(relativePos).coerceIn(0.0, screenSize.x - size.x, 0.0, screenSize.y - size.y) + set(value) { relativePos = absToRelative(value.roundToStep(ClickGui.dockingGridSize)); if (autoDocking) autoDocking() } + + private val dockingOffset get() = (screenSize - size) * Vec2d(dockingH.multiplier, dockingV.multiplier) + + private fun relativeToAbs(posIn: Vec2d) = posIn + dockingOffset + private fun absToRelative(posIn: Vec2d) = posIn - dockingOffset + + fun autoDocking() { + val screenCenterX = (screenSize.x * 0.3333)..(screenSize.x * 0.6666) + val screenCenterY = (screenSize.y * 0.3333)..(screenSize.y * 0.6666) + + val drawableCenter = dockingBase + + dockingH = if (allowHAlign) { + when { + drawableCenter.x < screenCenterX.start -> HAlign.LEFT + drawableCenter.x > screenCenterX.endInclusive -> HAlign.RIGHT + else -> HAlign.CENTER + } + } else HAlign.LEFT + + dockingV = if (allowVAlign) { + when { + drawableCenter.y < screenCenterY.start -> VAlign.TOP + drawableCenter.y > screenCenterY.endInclusive -> VAlign.BOTTOM + else -> VAlign.CENTER + } + } else VAlign.TOP + } + + enum class HAlign(val multiplier: Double) { + LEFT(0.0), + CENTER(0.5), + RIGHT(1.0) + } + + enum class VAlign(val multiplier: Double) { + TOP(0.0), + CENTER(0.5), + BOTTOM(1.0) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildLayer.kt index f27b4329e..a1ec50eff 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildLayer.kt @@ -28,7 +28,7 @@ open class ChildLayer( childAccessible(child) && child.rect in rect && ownerAccessible && ownerComponent.isActive } - is GuiEvent.KeyPress, is GuiEvent.CharTyped -> { + is GuiEvent.KeyPress, is GuiEvent.CharTyped, is GuiEvent.MouseScroll -> { if (!child.accessible) return@forEach } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt index f2cc6a76b..aca17d474 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt @@ -5,10 +5,12 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.graphics.buffer.FrameBuffer import com.lambda.graphics.shader.Shader import com.lambda.gui.AbstractGuiConfigurable +import com.lambda.gui.GuiConfigurable import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.gui.impl.clickgui.buttons.SettingButton import com.lambda.gui.impl.clickgui.windows.ModuleWindow import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow @@ -18,6 +20,8 @@ import com.lambda.module.Module import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall +import kotlin.reflect.KMutableProperty +import kotlin.reflect.KMutableProperty0 abstract class AbstractClickGui(name: String, owner: Module? = null) : LambdaGui(name, owner) { protected var hoveredWindow: WindowComponent<*>? = null @@ -97,7 +101,9 @@ abstract class AbstractClickGui(name: String, owner: Module? = null) : LambdaGui } } - private inline fun syncWindows(configWindows: MutableList) = windows.apply { + private inline fun syncWindows(prop: KMutableProperty0>) = windows.apply { + var configWindows by prop + // Add windows from config configWindows.filter { it !in children }.forEach(children::add) @@ -107,15 +113,14 @@ abstract class AbstractClickGui(name: String, owner: Module? = null) : LambdaGui } // Update config - configWindows.clear() - configWindows.addAll(children.filterIsInstance()) + configWindows = children.filterIsInstance().toMutableList() } - fun updateWindows() { - syncWindows(configurable.mainWindows) + private fun updateWindows() { + syncWindows(configurable::mainWindows) - if (this != LambdaHudGui) { - syncWindows(configurable.customWindows) + (configurable as? GuiConfigurable)?.let { + syncWindows(it::customWindows) } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt index 1a03a8ae9..86ec9933c 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt @@ -96,6 +96,8 @@ class ModuleButton( is GuiEvent.Render -> { super.onEvent(e) + settingsRenderer.font.scaleMultiplier = ClickGui.settingsFontScale + // Shadow renderer.filled.apply { val rect = Rect( diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt index 859464aac..b4b3ca532 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt @@ -38,7 +38,7 @@ class NumberSlider( } } - override fun getText() = "$setting" + override fun getText() = "$setting".replace(',', '.') // "0,0".toDouble() is null override fun setStringValue(string: String) { string.toDoubleOrNull()?.let(::setValue) } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt index cf5433d95..f8d2e450b 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt @@ -2,6 +2,9 @@ package com.lambda.gui.impl.clickgui.windows.tag import com.lambda.gui.impl.AbstractClickGui import com.lambda.gui.impl.clickgui.windows.ModuleWindow +import com.lambda.gui.impl.hudgui.LambdaHudGui +import com.lambda.module.HudModule +import com.lambda.module.Module import com.lambda.module.ModuleRegistry import com.lambda.module.tag.ModuleTag @@ -9,6 +12,10 @@ class TagWindow( val tag: ModuleTag, owner: AbstractClickGui, ) : ModuleWindow(tag.name, gui = owner) { + val isHudWindow = gui is LambdaHudGui + private val rawFilter = { m: Module -> m is HudModule } + private val filter get() = if (isHudWindow) rawFilter else { m: Module -> !rawFilter(m) } + override fun getModuleList() = ModuleRegistry.modules - .filter { it.defaultTags.firstOrNull() == tag } + .filter { it.defaultTags.firstOrNull() == tag && filter(it) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index c1922f55e..05f26ca5b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -50,9 +50,6 @@ object PlayerPacketManager : Loadable { val (yaw, pitch) = rotation.float val onGround = new.onGround - // Fix sensitivity for absolutely any outgoing angle - RotationManager.currentRotation = rotation.fixSensitivity(RotationManager.prevRotation) - if (player.hasVehicle()) { connection.sendPacket( Full( diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index 60fe0b57d..9f1f4f7f3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -10,6 +10,7 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.event.listener.UnsafeListener.Companion.unsafeListener 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.RotationContext import com.lambda.interaction.rotation.RotationMode @@ -19,34 +20,58 @@ import com.lambda.threading.runSafe import com.lambda.util.math.MathUtils.lerp import com.lambda.util.math.MathUtils.toRadian import com.lambda.util.math.Vec2d -import com.lambda.util.player.MovementUtils.handledByBaritone import com.lambda.util.primitives.extension.partialTicks import com.lambda.util.primitives.extension.rotation +import net.minecraft.client.input.Input import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket -import net.minecraft.util.math.MathHelper -import kotlin.math.roundToInt -import kotlin.math.sign +import kotlin.math.* object RotationManager : Loadable { var currentRotation = Rotation.ZERO var prevRotation = Rotation.ZERO - var currentContext: RotationContext? = null + private var currentContext: RotationContext? = null private var keepTicks = 0 private var pauseTicks = 0 - init { - listener { - RotationEvent.Update(BaritoneProcessor.poolContext()).post { - rotate(context) + fun Any.requestRotation( + priority: Int = 0, + alwaysListen: Boolean = false, + onUpdate: SafeContext.() -> RotationContext?, + onReceive: SafeContext.() -> Unit + ) { + var lastCtx: RotationContext? = null - currentContext?.let { - RotationEvent.Post(it).post() - } + this.listener(priority, alwaysListen) { event -> + val rotationContext = onUpdate() + + rotationContext?.let { + event.context = it + } + + lastCtx = rotationContext + } + + this.listener { event -> + if (event.context == lastCtx && event.context.isValid) { + onReceive() + } + } + } + + @JvmStatic + fun update() = runSafe { + RotationEvent.Update(BaritoneProcessor.poolContext()).post { + rotate(context) + + currentContext?.let { + RotationEvent.Post(it).post() } } + } + init { listener { event -> val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listener @@ -88,6 +113,7 @@ object RotationManager : Loadable { currentRotation .slerp(rotationTo, turnSpeed) + .fixSensitivity(prevRotation) .apply { if (context.config.rotationMode != RotationMode.LOCK) return@apply player.yaw = this.yawF @@ -174,12 +200,6 @@ object RotationManager : Loadable { 270.0, 315.0, ) - init { - listener(Int.MAX_VALUE) { - processPlayerMovement(it) - } - } - @JvmStatic fun handleBaritoneRotation(yaw: Float, pitch: Float) { baritoneContext = RotationContext(Rotation(yaw, pitch), Baritone.rotation.apply { @@ -188,47 +208,49 @@ object RotationManager : Loadable { }) } - private fun SafeContext.processPlayerMovement(event: MovementEvent.InputUpdate) { - val config = currentContext?.config ?: return + @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 strafeEvent = RotationEvent.StrafeInput(baritoneYaw ?: player.yaw.toDouble(), input) + val movementYaw = strafeEvent.post().strafeYaw - val input = event.input + // No changes are needed, when we don't modify the yaw used to move the player + // val config = currentContext?.config ?: return@runSafe + // if (config.rotationMode == RotationMode.SILENT && !input.handledByBaritone && baritoneContext == null) return@runSafe // Sign it to remove previous speed modifier val signForward = sign(input.movementForward) val signStrafe = sign(input.movementSideways) // No changes are needed when no inputs are pressed - if (signForward == 0f && signStrafe == 0f) return + if (signForward == 0f && signStrafe == 0f) return@runSafe - // Movement speed modifier - val multiplier = if (event.slowDown) event.slowDownFactor else 1f + // Actual yaw used by the physics engine + var actualYaw = currentRotation.yaw - // No changes are needed, when we don't modify the yaw used to move the player - if (config.rotationMode == RotationMode.SILENT && !input.handledByBaritone && baritoneContext == null) return - - // The yaw relative to which the movement was constructed - val baritoneYaw = baritoneContext?.rotation?.yaw - val strafeEvent = RotationEvent.Strafe(baritoneYaw ?: player.yaw.toDouble(), input) - val movementYaw = strafeEvent.post().strafeYaw - - // Actual yaw used to move the player - val actualYaw = currentRotation.yaw + if (currentContext?.config?.rotationMode == RotationMode.SILENT) { + actualYaw = player.yaw.toDouble() + } - val yawRad = (movementYaw - actualYaw).toRadian().toFloat() + val yawRad = (movementYaw - actualYaw).toRadian() - val cosDelta = MathHelper.cos(yawRad) - val sinDelta = MathHelper.sin(yawRad) + val cosDelta = cos(yawRad) + val sinDelta = sin(yawRad) val newX = signStrafe * cosDelta - signForward * sinDelta val newZ = signForward * cosDelta + signStrafe * sinDelta // Apply new movement input.apply { - movementSideways = newX.roundToInt().toFloat() * multiplier - movementForward = newZ.roundToInt().toFloat() * multiplier + // Movement speed modifier + val multiplier = if (slowDown) slowDownFactor else 1f + + movementSideways = round(newX).toFloat() * multiplier + movementForward = round(newZ).toFloat() * multiplier } - baritoneYaw ?: return + baritoneYaw ?: return@runSafe // Makes baritone movement safe // when yaw difference is too big to compensate it by modifying keyboard input 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 b75318aaf..29af0d786 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 @@ -12,7 +12,7 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.ContainerManager.findBestAvailableTool import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationContext -import com.lambda.interaction.visibilty.VisibilityChecker.mostCenter +import com.lambda.interaction.visibilty.VisibilityChecker.optimum import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces import com.lambda.module.modules.client.TaskFlow import com.lambda.threading.runSafe @@ -160,7 +160,7 @@ object BuildSimulator { return@forEach } - validHits.keys.mostCenter?.let { optimum -> + validHits.keys.optimum?.let { optimum -> validHits.minByOrNull { optimum distSq it.key }?.let { closest -> val optimumRotation = eye.rotationTo(closest.key) RotationContext(optimumRotation, rotation, closest.value, verify) @@ -368,7 +368,7 @@ object BuildSimulator { } } - validHits.keys.mostCenter?.let { optimum -> + validHits.keys.optimum?.let { optimum -> validHits.minByOrNull { optimum distSq it.key }?.let { closest -> val optimumRotation = eye.rotationTo(closest.key) RotationContext(optimumRotation, rotation, closest.value, verify) 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 b462e22cb..321dac45f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt @@ -4,9 +4,13 @@ import com.lambda.Lambda.mc import com.lambda.threading.runSafe import com.lambda.util.math.MathUtils.toDegree import com.lambda.util.math.MathUtils.toRadian +import com.lambda.util.math.Vec2d +import com.lambda.util.math.VecUtils.plus +import com.lambda.util.math.VecUtils.times import com.lambda.util.world.raycast.RayCastMask import com.lambda.util.world.raycast.RayCastUtils.rayCast import net.minecraft.entity.Entity +import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper import net.minecraft.util.math.Vec3d @@ -42,6 +46,15 @@ data class Rotation(val yaw: Double, val pitch: Double) { rayCast(eye ?: player.eyePos, vector, reach, mask, fluids) } + fun castBox( + box: Box, + reach: Double, + eye: Vec3d? = null + ) = runSafe { + val eyeVec = eye ?: player.eyePos + box.raycast(eyeVec, eyeVec + vector * reach).orElse(null) + } + val Direction.yaw: Float get() = when (this) { Direction.NORTH -> -180.0f @@ -79,20 +92,16 @@ data class Rotation(val yaw: Double, val pitch: Double) { return Rotation(yaw, pitch) } - fun Rotation.fixSensitivity(last: Rotation): Rotation { + fun Rotation.fixSensitivity(prev: Rotation): Rotation { val f = mc.options.mouseSensitivity.value * 0.6 + 0.2 - val step = f * f * f * 8.0 * 0.15F - - val deltaYaw = yaw - last.yaw - var fixedYaw = (deltaYaw / step).roundToInt() * step - fixedYaw += last.yaw + val gcd = f * f * f * 8.0 * 0.15F - val deltaPitch = pitch - last.pitch - var fixedPitch = (deltaPitch / step).roundToInt() * step - fixedPitch += last.pitch - fixedPitch = fixedPitch.coerceIn(-90.0, 90.0) + val r1 = Vec2d(prev.yaw, prev.pitch) + val r2 = Vec2d(this.yaw, this.pitch) + val delta = ((r2 - r1) / gcd).roundToInt() * gcd + val fixed = r1 + delta - return Rotation(fixedYaw, fixedPitch) + return Rotation(fixed.x, fixed.y.coerceIn(-90.0, 90.0)) } fun Vec3d.rotationTo(vec: Vec3d): Rotation { @@ -109,7 +118,7 @@ data class Rotation(val yaw: Double, val pitch: Double) { return Rotation(yaw, pitch) } - fun Rotation.distance(b: Rotation) = + infix fun Rotation.dist(b: Rotation) = hypot( wrap(yaw - b.yaw), wrap(pitch - b.pitch) 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 0d55a028a..a35216e07 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt @@ -1,6 +1,7 @@ package com.lambda.interaction.rotation import com.lambda.config.groups.IRotationConfig +import com.lambda.util.world.raycast.RayCastUtils.orMiss import net.minecraft.util.hit.HitResult data class RotationContext( @@ -9,5 +10,5 @@ data class RotationContext( val hitResult: HitResult? = null, val verify: HitResult.() -> Boolean = { true }, ) { - val isValid: Boolean get() = hitResult?.verify() == true + val isValid: Boolean get() = verify(hitResult.orMiss) } \ 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/visibilty/VisibilityChecker.kt index 5e8d6a6f2..ae3f9f118 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -4,6 +4,7 @@ import com.lambda.config.groups.IRotationConfig import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext import com.lambda.interaction.RotationManager +import com.lambda.interaction.rotation.Rotation.Companion.dist import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationContext import com.lambda.module.modules.client.TaskFlow @@ -46,37 +47,42 @@ object VisibilityChecker { fun SafeContext.findRotation( boxes: List, - rotationConfig: IRotationConfig = TaskFlow.rotation, - interact: InteractionConfig = TaskFlow.interact, + rotationConfig: IRotationConfig, + interact: InteractionConfig, sides: Set = emptySet(), + reach: Double = interact.reach, + eye: Vec3d = player.getCameraPosVec(1f), verify: HitResult.() -> Boolean, ): RotationContext? { - val eye = player.getCameraPosVec(mc.tickDelta) - val currentRotation = RotationManager.currentRotation - val currentCast = currentRotation.rayCast(interact.reach, eye) + val currentCast = currentRotation.rayCast(reach, eye) if (boxes.any { it.contains(eye) }) { return RotationContext(currentRotation, rotationConfig, currentCast, verify) } val validHits = mutableMapOf() - val reachSq = interact.reach.pow(2) + val reachSq = reach.pow(2) boxes.forEach { box -> - scanVisibleSurfaces(player.eyePos, box, sides, interact.resolution) { _, vec -> + scanVisibleSurfaces(eye, box, sides, interact.resolution) { _, vec -> if (eye distSq vec > reachSq) return@scanVisibleSurfaces val newRotation = eye.rotationTo(vec) - val cast = newRotation.rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces + val cast = newRotation.rayCast(reach, eye) ?: return@scanVisibleSurfaces if (!cast.verify()) return@scanVisibleSurfaces validHits[vec] = cast } } - validHits.keys.mostCenter?.let { optimum -> + // Way stable + /*validHits.minByOrNull { eye.rotationTo(it.key) dist currentRotation }?.let { closest -> + return RotationContext(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 RotationContext(optimumRotation, rotationConfig, closest.value, verify) @@ -93,11 +99,10 @@ object VisibilityChecker { resolution: Int, check: (Direction, Vec3d) -> Unit, ) { - val shrunk = box.expand(-0.005) box.getVisibleSurfaces(eyes) .forEach { side -> if (sides.isNotEmpty() && side !in sides) return@forEach - val (minX, minY, minZ, maxX, maxY, maxZ) = shrunk.bounds(side) + val (minX, minY, minZ, maxX, maxY, maxZ) = box.shrink(0.01, 0.01, 0.01).bounds(side) val stepX = (maxX - minX) / resolution val stepY = (maxY - minY) / resolution val stepZ = (maxZ - minZ) / resolution @@ -112,7 +117,7 @@ object VisibilityChecker { } } - val Set.mostCenter: Vec3d? + val Set.optimum: Vec3d? get() = reduceOrNull { acc, vec3d -> acc.add(vec3d) }?.multiply(1.0 / size.toDouble()) diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt index 52b0a9e07..3bdef54db 100644 --- a/common/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -1,12 +1,14 @@ package com.lambda.module import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.animation.AnimationTicker +import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.RenderLayer +import com.lambda.gui.api.component.core.DockingRect import com.lambda.module.tag.ModuleTag import com.lambda.util.KeyCode -import com.lambda.util.math.MathUtils.coerceIn -import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d abstract class HudModule( @@ -21,45 +23,40 @@ abstract class HudModule( protected abstract val width: Double protected abstract val height: Double - private val size get() = Vec2d(width, height) - private var relativePosX by setting("Position X", 0.0, -10000.0..10000.0, 0.1) { false } - private var relativePosY by setting("Position Y", 0.0, -10000.0..10000.0, 0.1) { false } - private var relativePos get() = Vec2d(relativePosX, relativePosY) - set(value) { relativePosX = value.x; relativePosY = value.y } + private val rectHandler = object : DockingRect() { + private var relativePosX by setting("Position X", 0.0, -10000.0..10000.0, 0.1) { false } + private var relativePosY by setting("Position Y", 0.0, -10000.0..10000.0, 0.1) { false } + override var relativePos get() = Vec2d(relativePosX, relativePosY); set(value) { relativePosX = value.x; relativePosY = value.y } - var position - get() = relativeToAbs(relativePos).coerceIn(0.0, screenSize.x - width, 0.0, screenSize.y - height) - set(value) { relativePos = absToRelative(value); if (autoDocking) autoDocking() } + override val width get() = this@HudModule.width + override val height get() = this@HudModule.height - private val dockingOffset get() = (screenSize - size) * Vec2d(dockingH.multiplier, dockingV.multiplier) - - private fun relativeToAbs(posIn: Vec2d) = posIn + dockingOffset - private fun absToRelative(posIn: Vec2d) = posIn - dockingOffset - - private val autoDocking by setting("Auto Docking", true).apply { - onValueChange { _, _ -> - autoDocking() + override val autoDocking by setting("Auto Docking", true).apply { + onValueChange { _, _ -> + autoDocking() + } } - } - private var dockingH by setting("Docking H", HAlign.LEFT) { !autoDocking }.apply { - onValueChange { from, to -> - val delta = to.multiplier - from.multiplier - relativePosX += delta * (size.x - screenSize.x) + override var dockingH by setting("Docking H", HAlign.LEFT) { !autoDocking }.apply { + onValueChange { from, to -> + val delta = to.multiplier - from.multiplier + relativePosX += delta * (size.x - screenSize.x) + } } - } - private var dockingV by setting("Docking V", VAlign.TOP) { !autoDocking }.apply { - onValueChange { from, to -> - val delta = to.multiplier - from.multiplier - relativePosY += delta * (size.y - screenSize.y) + override var dockingV by setting("Docking V", VAlign.TOP) { !autoDocking }.apply { + onValueChange { from, to -> + val delta = to.multiplier - from.multiplier + relativePosY += delta * (size.y - screenSize.y) + } } } - val rect get() = Rect.basedOn(position, width, height) + var position by rectHandler::position + val rect by rectHandler::rect + val animation = AnimationTicker() - private var screenSize = Vec2d.ZERO private val renderer = RenderLayer() protected fun onRender(block: RenderLayer.() -> Unit) = @@ -67,7 +64,7 @@ abstract class HudModule( init { listener { event -> - screenSize = event.screenSize + rectHandler.screenSize = event.screenSize renderCallables.forEach { function -> function.invoke(renderer) @@ -75,36 +72,9 @@ abstract class HudModule( renderer.render() } - } - - private fun autoDocking() { - val screenCenterX = (screenSize.x * 0.3333)..(screenSize.x * 0.6666) - val screenCenterY = (screenSize.y * 0.3333)..(screenSize.y * 0.6666) - - val drawableCenter = rect.center - - dockingH = when { - drawableCenter.x < screenCenterX.start -> HAlign.LEFT - drawableCenter.x > screenCenterX.endInclusive -> HAlign.RIGHT - else -> HAlign.CENTER - } - dockingV = when { - drawableCenter.y < screenCenterY.start -> VAlign.TOP - drawableCenter.y > screenCenterY.endInclusive -> VAlign.BOTTOM - else -> VAlign.CENTER + listener { + animation.tick() } } - - enum class HAlign(val multiplier: Float) { - LEFT(0.0f), - CENTER(0.5f), - RIGHT(1.0f) - } - - enum class VAlign(val multiplier: Float) { - TOP(0.0f), - CENTER(0.5f), - BOTTOM(1.0f) - } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt b/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt new file mode 100644 index 000000000..fb7e8701e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt @@ -0,0 +1,55 @@ +package com.lambda.module.hud + +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.module.HudModule +import com.lambda.module.modules.client.ClickGui +import com.lambda.module.modules.client.GuiSettings +import com.lambda.module.modules.client.GuiSettings.primaryColor +import com.lambda.module.modules.movement.TickShift +import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.Rect +import java.awt.Color + +object TickShiftCharge : HudModule( + name = "TickShiftCharge", + defaultTags = setOf(ModuleTag.CLIENT), +) { + private val isActive get() = TickShift.isEnabled && TickShift.isActive && TickShift.boost + private val activeAnimation by animation.exp(0.0, 1.0, 0.6, ::isActive) + + private val progress get() = if (!TickShift.isActive) 0.0 + else (TickShift.balance / TickShift.maxBalance.toDouble()).coerceIn(0.0..1.0) + + private val renderProgress by animation.exp(::progress, 0.8) + + override val width = 70.0 + override val height = 14.0 + + init { + onRender { + filled.build( + rect = rect, + roundRadius = ClickGui.windowRadius, + color = GuiSettings.backgroundColor, + shade = GuiSettings.shadeBackground + ) + + val padding = 1.0 + filled.build( + rect = Rect.basedOn(rect.leftTop, rect.size.x * renderProgress, rect.size.y).shrink(padding), + roundRadius = ClickGui.windowRadius - padding, + color = GuiSettings.mainColor.multAlpha(0.3), + shade = true + ) + + outline.build( + rect = rect, + roundRadius = ClickGui.windowRadius, + color = (if (GuiSettings.shadeBackground) Color.WHITE else primaryColor).multAlpha(activeAnimation), + glowRadius = ClickGui.glowRadius * activeAnimation, + shade = true + ) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt b/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt index 0e631dc50..15f9190e9 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt @@ -20,7 +20,7 @@ object Watermark : HudModule( init { onRender { - if (shade) drawTextureShaded(monoTexture, rect, 0.1) + if (shade) drawTextureShaded(monoTexture, rect) else drawTexture(normalTexture, rect) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index 263531e42..49d01f942 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -21,10 +21,17 @@ object ClickGui : Module( val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1) val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1) val buttonStep by setting("Button Step", 0.0, 0.0..5.0, 0.1) + val settingsFontScale by setting("Settings Font Scale", 0.92, 0.5..1.0, 0.01) // Animation val openSpeed by setting("Open Speed", 0.5, 0.1..1.0, 0.01) val closeSpeed by setting("Close Speed", 0.5, 0.1..1.0, 0.01) + val scrollSpeed by setting("Scroll Speed", 1.0, 0.1..10.0, 0.01) + + // Alignment + val allowHAlign by setting("Allow H Docking", false) + val allowVAlign by setting("Allow V Docking", true) + val dockingGridSize by setting("Docking Grid Size", 1.0, 0.0..20.0, 0.5) init { onEnable { 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 new file mode 100644 index 000000000..9dc83d2ab --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/Criticals.kt @@ -0,0 +1,55 @@ +package com.lambda.module.modules.combat + +import com.lambda.context.SafeContext +import com.lambda.event.events.AttackEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.rotation.Rotation +import com.lambda.interaction.rotation.Rotation.Companion.rotationTo +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.primitives.extension.component1 +import com.lambda.util.primitives.extension.component2 +import com.lambda.util.primitives.extension.component3 +import com.lambda.util.primitives.extension.rotation +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +object Criticals : Module( + name = "Criticals", + description = "Forces your hits to be critical", + defaultTags = setOf(ModuleTag.COMBAT) +) { + private val mode by setting("Mode", Mode.Grim) + + enum class Mode { + Grim + } + + init { + listener { + when (mode) { + Mode.Grim -> { + if (player.isOnGround) posPacket(0.00000001, rotation = player.rotation) + posPacket(-0.000000001, rotation = player.eyePos.rotationTo(it.entity.boundingBox.center)) + + connection.sendPacket(PlayerInteractItemC2SPacket(Hand.OFF_HAND, 0)) + connection.sendPacket(PlayerActionC2SPacket(PlayerActionC2SPacket.Action.RELEASE_USE_ITEM, BlockPos.ORIGIN, Direction.DOWN)) + } + } + } + } + + private fun SafeContext.posPacket(yOffset: Double, ground: Boolean = false, rotation: Rotation?) { + val (x, y, z) = player.pos + + val packet = rotation?.let { + PlayerMoveC2SPacket.Full(x, y + yOffset, z, it.yawF, it.pitchF, ground) + } ?: PlayerMoveC2SPacket.PositionAndOnGround(x, y + yOffset, z, ground) + + connection.sendPacket(packet) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..a17f46a0e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -0,0 +1,311 @@ +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.PacketEvent +import com.lambda.event.events.PlayerPacketEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.RotationManager +import com.lambda.interaction.RotationManager.requestRotation +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.RotationContext +import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces +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.lerp +import com.lambda.util.math.MathUtils.random +import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.math.VecUtils.minus +import com.lambda.util.math.VecUtils.plus +import com.lambda.util.math.VecUtils.times +import com.lambda.util.player.MovementUtils.moveDiff +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 +import net.minecraft.entity.attribute.EntityAttributes +import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket +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 + +object KillAura : Module( + name = "KillAura", + description = "Attacks entities", + defaultTags = setOf(ModuleTag.COMBAT, ModuleTag.RENDER) +) { + private val page by setting("Page", Page.Interact) + + // Interact + private val interactionSettings = InteractionSettings(this, 3.0) { 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 } + private val timerSync by setting("Assume Timer", true) { page == Page.Interact && attackMode == AttackMode.Cooldown && delaySync } + private val cooldownOffset by setting("Cooldown Offset", 0, -5..5, 1) { page == Page.Interact && attackMode == AttackMode.Cooldown } + private val hitDelay1 by setting("Hit Delay 1", 2.0, 0.0..20.0, 1.0) { page == Page.Interact && attackMode == AttackMode.Delay } + private val hitDelay2 by setting("Hit Delay 2", 6.0, 0.0..20.0, 1.0) { page == Page.Interact && attackMode == AttackMode.Delay } + private val criticalSync by setting("Critical Sync", true) { page == Page.Interact && attackMode == AttackMode.Cooldown } + + // Targeting + private val targeting = Targeting.Combat(this) { page == Page.Targeting } + + // Aiming + private val rotate by setting("Rotate", true) { page == Page.Aiming } + private val rotation = RotationSettings(this) { page == Page.Aiming && rotate } + private val stabilize by setting("Stabilize", true) { page == Page.Aiming && !rotation.instant && rotate } + private val centerFactor by setting("Center Factor", 0.4, 0.0..1.0, 0.01) { page == Page.Aiming && rotate } + private val shakeFactor by setting("Shake Factor", 0.4, 0.0..1.0, 0.01) { page == Page.Aiming && rotate } + private val shakeChance by setting("Shake Chance", 0.2, 0.05..1.0, 0.01) { page == Page.Aiming && shakeFactor > 0.0 && rotate } + 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 + + private var shakeRandom = Vec3d.ZERO + + private var attackTicks = 0 + private var lastAttackTime = 0L + private var hitDelay = 100.0 + + private var prevY = 0.0 + private var lastY = 0.0 + private var lastOnGround = true + private var onGroundTicks = 0 + + enum class Page { + Interact, + Targeting, + Aiming + } + + enum class AttackMode { + Cooldown, + Delay + } + + init { + requestRotation( + onUpdate = { + if (!rotate) return@requestRotation null + + target?.let { target -> + buildRotation(target) + } + }, + onReceive = { + target?.let { entity -> + runAttack(entity) + } + } + ) + + listener(Int.MIN_VALUE) { event -> + prevY = lastY + lastY = event.position.y + lastOnGround = event.onGround + } + + listener { + target = targeting.getTarget() + if (!timerSync) attackTicks++ + + if (!rotate) { + target?.let { entity -> + runAttack(entity) + } + } + } + + runConcurrent { + while (true) { + delay(50) // ToDo: tps sync + + runSafe { + if (timerSync && isEnabled) attackTicks++ + } + } + } + + listener { event -> + if (event.packet !is HandSwingC2SPacket && + event.packet !is UpdateSelectedSlotC2SPacket && + event.packet !is PlayerInteractEntityC2SPacket + ) return@listener + + attackTicks = 0 + } + + onEnable(::reset) + onDisable(::reset) + } + + private fun SafeContext.buildRotation(target: LivingEntity): RotationContext? { + val currentRotation = RotationManager.currentRotation + + val eye = player.getCameraPosVec(1f) + 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 RotationContext(currentRotation, rotation) + } + + // Rotation stabilizer + rotation.speedMultiplier = if (stabilize && !rotation.instant) { + val slowDown = currentRotation.castBox(box, reach) != null + + with(rotation) { + val targetSpeed = if (slowDown) 0.0 else 1.0 + val acceleration = if (slowDown) 0.2 else 0.1 + + targetSpeed.coerceIn( + speedMultiplier - acceleration, + speedMultiplier + acceleration + ) + } + } else 1.0 + + // Update shake vector + if (random(0.0, 1.0) < shakeChance) { + shakeRandom = Vec3d( + random(0.0, 1.0), + random(0.0, 1.0), + random(0.0, 1.0), + ) + } + + // Find the closest point to the player's eyes + var vec = Vec3d( + eye.x.coerceIn(box.minX, box.maxX), + eye.y.coerceIn(box.minY, box.maxY), + eye.z.coerceIn(box.minZ, box.maxZ) + ) + + val random = Vec3d( + lerp(box.minX, box.maxX, shakeRandom.x), + lerp(box.minY, box.maxY, shakeRandom.x), + lerp(box.minZ, box.maxZ, shakeRandom.x) + ) + + vec = lerp(vec, box.center, centerFactor) // Mix with center + vec = lerp(vec, random, shakeFactor) // Apply shaking + + // Raycast + run { + if (!interactionSettings.useRayCast) return@run + + val vecRotation = eye.rotationTo(vec) + if (vecRotation.rayCast(reach, eye)?.entityResult?.entity == target) return@run + + // Get visible point set + val validHits = mutableMapOf() + + scanVisibleSurfaces(eye, box, emptySet(), interactionSettings.resolution) { _, vec -> + if (eye distSq vec > reachSq) return@scanVisibleSurfaces + + val newRotation = eye.rotationTo(vec) + + val cast = newRotation.rayCast(reach, eye) ?: return@scanVisibleSurfaces + if (cast.entityResult?.entity != target) return@scanVisibleSurfaces + + validHits[vec] = newRotation + } + + // Switch to the closest visible point + vec = validHits.minByOrNull { vecRotation dist it.value }?.key ?: return null + } + + val predictOffset = target.moveDiff * targetPredict - player.moveDiff * Vec3d(1.0, -0.5, 1.0) * selfPredict + return RotationContext(eye.rotationTo(vec + predictOffset), rotation) + } + + private fun SafeContext.runAttack(target: LivingEntity) { + // Critical hit check + run { + if (!criticalSync || attackMode != AttackMode.Cooldown) return@run + + onGroundTicks++ + if (!lastOnGround) onGroundTicks = 0 + + val motionY = lastY - prevY + if (motionY > -0.05 || (lastOnGround && onGroundTicks < 5)) return + } + + // Cooldown check + run { + when (attackMode) { + AttackMode.Cooldown -> { + val attackedTicks = if (delaySync) attackTicks else player.lastAttackedTicks + if (attackedTicks < getAttackCooldown() + cooldownOffset) return + } + + AttackMode.Delay -> { + if (System.currentTimeMillis() - lastAttackTime < hitDelay) return + } + } + } + + // Rotation check + run { + if (!rotate) return@run + val angle = RotationManager.currentRotation + + if (interactionSettings.useRayCast) { + val cast = angle.rayCast(interactionSettings.reach) + if (cast?.entityResult?.entity != target) return + } + + // Perform a raycast without checking the environment + angle.castBox(target.boundingBox, interactionSettings.reach) ?: return + } + + // Attack + interaction.attackEntity(player, target) + if (interactionSettings.swingHand) player.swingHand(Hand.MAIN_HAND) + + lastAttackTime = System.currentTimeMillis() + hitDelay = random(hitDelay1, hitDelay2) * 50 + } + + private fun SafeContext.getAttackCooldown(): Double { + val attr = EntityAttributes.GENERIC_ATTACK_SPEED + + val attackSpeed = if (!cooldownSync) player.getAttributeValue(attr) else { + player.mainHandStack.item + .getAttributeModifiers(EquipmentSlot.MAINHAND)[attr] + .filter { it.operation == EntityAttributeModifier.Operation.ADDITION } + .sumOf { it.value } + 4 + } + + return 20.0 / attackSpeed + } + + private fun reset(ctx: SafeContext) = ctx.apply { + target = null + attackTicks = player.lastAttackedTicks + rotation.speedMultiplier = 1.0 + shakeRandom = Vec3d.ZERO + + lastY = 0.0 + prevY = 0.0 + lastOnGround = true + onGroundTicks = 0 + + lastAttackTime = 0L + hitDelay = 100.0 + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..5b3fe71d2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt @@ -0,0 +1,160 @@ +package com.lambda.module.modules.movement + +import com.lambda.context.SafeContext +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.PacketEvent +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.renderer.esp.DynamicAABB +import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.module.Module +import com.lambda.module.modules.client.GuiSettings +import com.lambda.module.modules.combat.KillAura +import com.lambda.module.tag.ModuleTag +import com.lambda.util.PacketUtils.handlePacketSilently +import com.lambda.util.ServerPacket +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.VecUtils.dist +import com.lambda.util.math.VecUtils.minus +import com.lambda.util.math.VecUtils.plus +import net.minecraft.network.packet.s2c.play.EntityAnimationS2CPacket +import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket +import net.minecraft.network.packet.s2c.play.EntityS2CPacket +import net.minecraft.network.packet.s2c.play.EntityStatusEffectS2CPacket +import net.minecraft.network.packet.s2c.play.ParticleS2CPacket +import net.minecraft.network.packet.s2c.play.PlaySoundFromEntityS2CPacket +import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket +import net.minecraft.network.packet.s2c.play.StopSoundS2CPacket +import net.minecraft.network.packet.s2c.play.WorldEventS2CPacket +import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket +import net.minecraft.util.math.Vec3d +import java.awt.Color +import java.util.concurrent.ConcurrentLinkedDeque + +object BackTrack : Module( + name = "BackTrack", + description = "Gives reach advantage by delaying your packets", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + private val mode by setting("Mode", Mode.FIXED) + private val delay by setting("Delay", 500, 100..2000) { mode == Mode.FIXED } + private val maxDelay by setting("Max Delay", 1000, 100..2000) { mode == Mode.RANGED || mode == Mode.ADAPTIVE } + private val distance by setting("Distance", 3.0, 1.0..5.0, 0.1) { mode == Mode.RANGED || mode == Mode.ADAPTIVE } + + private val target get() = if (KillAura.isDisabled) null else KillAura.target + private var targetPos: Vec3d? = null + + private val box = DynamicAABB() + + private const val POSITION_PACKET_SCALE = 1 / 4096.0 + private val currentTime get() = System.currentTimeMillis() + private val packetPool = ConcurrentLinkedDeque>() + + enum class Mode(val shouldSend: SafeContext.(Vec3d, Vec3d, Long) -> Boolean) { + FIXED({ _, _, timing -> + currentTime > timing + delay + }), + RANGED({ _, serverPos, timing -> + val serverDist = player.pos dist serverPos + currentTime > timing + maxDelay * serverDist.coerceIn(0.0, distance) / distance + }), + ADAPTIVE({ clientPos, serverPos, timing -> + val clientDist = player.pos dist clientPos + val serverDist = player.pos dist serverPos + val advantage = serverDist - clientDist + currentTime > timing + maxDelay * advantage.coerceIn(0.0, distance) / distance + }) + } + + init { + listener { + target?.let { target -> + val pos = targetPos ?: target.pos + targetPos = pos + + box.update(target.boundingBox.offset(pos - target.pos)) + poolPackets() + return@listener + } + + poolPackets(true) + targetPos = null + box.reset() + } + + listener { + val target = target ?: return@listener + + val c1 = GuiSettings.primaryColor + val c2 = Color.RED + val p = target.hurtTime / 10.0 + val c = lerp(c1, c2, p) + + it.renderer.build(box, c.multAlpha(0.3), c.multAlpha(0.8)) + } + + listener { event -> + val target = target ?: return@listener + + val packet = event.packet + + when (packet) { + is EntityS2CPacket -> { + if (target.id == packet.id) { + targetPos = targetPos?.plus( + Vec3d( + packet.deltaX * POSITION_PACKET_SCALE, + packet.deltaY * POSITION_PACKET_SCALE, + packet.deltaZ * POSITION_PACKET_SCALE + ) + ) + } + } + + is EntityPositionS2CPacket -> { + if (target.id == packet.id) { + targetPos = Vec3d(packet.x, packet.y, packet.z) + } + } + + is PlaySoundS2CPacket, is PlaySoundFromEntityS2CPacket, is StopSoundS2CPacket, + /*is EntityStatusS2CPacket,*/ is EntityStatusEffectS2CPacket, is EntityAnimationS2CPacket, + is ParticleS2CPacket, is WorldTimeUpdateS2CPacket, is WorldEventS2CPacket -> { + return@listener + } + } + + packetPool.add(packet to currentTime) + event.cancel() + } + + listener { + packetPool.clear() + } + + onEnable { + poolPackets(true) + } + + onDisable { + poolPackets(true) + } + } + + private fun SafeContext.poolPackets(all: Boolean = false) { + while (packetPool.isNotEmpty()) { + val (packet, timing) = packetPool.poll() ?: break + + val send = all || targetPos?.let { serverPos -> + target?.pos?.let { clientPos -> + mode.shouldSend(this, clientPos, serverPos, timing) + } + } ?: true + + if (!send) break + connection.handlePacketSilently(packet) + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..ddae9757b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt @@ -0,0 +1,98 @@ +package com.lambda.module.modules.movement + +import com.lambda.context.SafeContext +import com.lambda.event.events.PacketEvent +import com.lambda.event.events.RenderEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.renderer.esp.DynamicAABB +import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.module.Module +import com.lambda.module.modules.client.GuiSettings +import com.lambda.module.modules.combat.KillAura +import com.lambda.module.tag.ModuleTag +import com.lambda.util.ClientPacket +import com.lambda.util.PacketUtils.handlePacketSilently +import com.lambda.util.PacketUtils.sendPacketSilently +import com.lambda.util.math.ColorUtils.setAlpha +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket +import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import java.util.concurrent.ConcurrentLinkedDeque + +object Blink : Module( + name = "Blink", + description = "Holds packets", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + private var delay by setting("Delay", 500, 50..10000, 10) + private val shiftVelocity by setting("Shift velocity", true) + private val requiresAura by setting("Requires Aura", false) + + private val isActive get() = (KillAura.isEnabled && KillAura.target != null) || !requiresAura + + private var packetPool = ConcurrentLinkedDeque() + private var lastVelocity: EntityVelocityUpdateS2CPacket? = null + private var lastUpdate = 0L + + private var box = DynamicAABB() + private var lastBox = Box(BlockPos.ORIGIN) + + init { + listener { + val time = System.currentTimeMillis() + + if (isActive && time - lastUpdate < delay) return@listener + lastUpdate = time + + poolPackets() + } + + listener { event -> + val color = GuiSettings.primaryColor + event.renderer.build(box.update(lastBox), color.setAlpha(0.3), color) + } + + listener { event -> + if (!isActive) return@listener + + packetPool.add(event.packet) + event.cancel() + return@listener + } + + listener { event -> + if (!isActive || !shiftVelocity) return@listener + + if (event.packet !is EntityVelocityUpdateS2CPacket) return@listener + if (event.packet.id != player.id) return@listener + + lastVelocity = event.packet + event.cancel() + return@listener + } + + onDisable { + poolPackets() + } + } + + private fun SafeContext.poolPackets() { + while (packetPool.isNotEmpty()) { + packetPool.poll().let { packet -> + connection.sendPacketSilently(packet) + + if (packet is PlayerMoveC2SPacket && packet.changesPosition()) { + lastBox = player.boundingBox + .offset(player.pos.negate()) + .offset(packet.getX(0.0), packet.getY(0.0), packet.getZ(0.0)) + } + } + } + + lastVelocity?.let { velocity -> + connection.handlePacketSilently(velocity) + lastVelocity = null + } + } +} 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 new file mode 100644 index 000000000..51f91ba63 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Jesus.kt @@ -0,0 +1,153 @@ +package com.lambda.module.modules.movement + +import com.lambda.context.SafeContext +import com.lambda.event.events.MovementEvent +import com.lambda.event.events.PlayerPacketEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.Nameable +import com.lambda.util.math.MathUtils.toInt +import com.lambda.util.math.VecUtils.minus +import com.lambda.util.player.MovementUtils.isInputting +import com.lambda.util.player.MovementUtils.motionY +import com.lambda.util.player.MovementUtils.setSpeed +import net.minecraft.block.Blocks +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d +import net.minecraft.util.shape.VoxelShapes + +object Jesus : Module( + name = "Jesus", + description = "Allows to walk on water", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + private val mode by setting("Mode", Mode.NCP) + + // Dolphin + private val dolphinStrength by setting("Dolphin Strength", 0.1, 0.01..0.2, 0.01) { mode == Mode.NCP_DOLPHIN } + + // NCP New + private val slowDown by setting("Slow Down", true) { mode == Mode.NCP_NEW } + + private val fullShape = VoxelShapes.fullCube() + private var goUp = true + private var swimmingTicks = 0 + + enum class Mode(override val displayName: String, val collision: Boolean) : Nameable.NamedEnum { + NCP("NCP", true), + NCP_DOLPHIN("NCP Dolphin", false), + NCP_NEW("NCP New", true) + } + + private var shouldWork = false + + init { + listener { event -> + if (!shouldWork || !waterAt(-0.0001)) return@listener + event.onGround = false + + if (!player.isOnGround) return@listener + + when (mode) { + Mode.NCP -> { + val offset = if (player.age % 2 == 0) 0.001 else 0.002 + event.position -= Vec3d(0.0, offset, 0.0) + } + + Mode.NCP_NEW-> { + event.position -= Vec3d(0.0, 0.02 + 0.0001 * swimmingTicks, 0.0) + } + + else -> {} + } + } + + listener { + if (!shouldWork) return@listener + + goUp = waterAt(0.0001) + val collidingWater = waterAt(-0.0001) + + when (mode) { + Mode.NCP -> { + if (!collidingWater || !player.isOnGround) return@listener + setSpeed(Speed.NCP_BASE_SPEED * isInputting.toInt()) + } + + Mode.NCP_DOLPHIN -> { + if (goUp) { + player.motionY = dolphinStrength + + if (!waterAt(0.2)) { + setSpeed(Speed.NCP_BASE_SPEED * isInputting.toInt()) + } else player.motionY = 0.18 + } + } + + Mode.NCP_NEW -> { + if (!collidingWater) { + swimmingTicks = 0 + return@listener + } + + if (++swimmingTicks < 15) { + if (player.isOnGround) { + setSpeed(Speed.NCP_BASE_SPEED * isInputting.toInt()) + } + + return@listener + } + + swimmingTicks = 0 + + if (slowDown) setSpeed(0.0) + player.motionY = 0.08000001 + } + } + } + + listener { event -> + if (!shouldWork || goUp || !mode.collision) return@listener + + if (event.state.block == Blocks.WATER) { + event.shape = fullShape + } + } + + listener { + if (!shouldWork || !goUp || mode == Mode.NCP_DOLPHIN) return@listener + it.input.jumping = true + } + + listener { + shouldWork = !player.abilities.flying && !player.isFallFlying && !player.input.sneaking + } + + onEnable { + goUp = false + swimmingTicks = 0 + shouldWork = false + } + } + + private fun SafeContext.waterAt(yOffset: Double) : Boolean { + val b = player.boundingBox + val y = b.minY + yOffset + + for (xref in 0..1) { + for (zref in 0..1) { + val x = if (xref == 0) b.minX else b.maxX + val z = if (zref == 0) b.minZ else b.maxZ + + val pos = BlockPos.ofFloored(x, y, z) + val state = world.getBlockState(pos) + if (state.block != Blocks.WATER) return false + } + } + + return true + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/NoFall.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/NoFall.kt new file mode 100644 index 000000000..73f630a8f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/NoFall.kt @@ -0,0 +1,49 @@ +package com.lambda.module.modules.movement + +import com.lambda.event.events.MovementEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.player.MovementUtils.motion +import com.lambda.util.player.MovementUtils.motionY +import com.lambda.util.primitives.extension.component1 +import com.lambda.util.primitives.extension.component2 +import com.lambda.util.primitives.extension.component3 +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d + +object NoFall : Module( + name = "NoFall", + description = "Reduces fall damage", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + private val mode by setting("Mode", Mode.Grim) + + enum class Mode { + Grim + } + + init { + listener { + when (mode) { + Mode.Grim -> { + if (player.fallDistance + player.motionY < 3.0) return@listener + + val (x, y, z) = player.pos + connection.sendPacket(PlayerMoveC2SPacket.Full(x, y + 0.0000000001, z, 0.01f, 90f, false)) + connection.sendPacket(PlayerInteractItemC2SPacket(Hand.OFF_HAND, 0)) + connection.sendPacket(PlayerActionC2SPacket(PlayerActionC2SPacket.Action.RELEASE_USE_ITEM, BlockPos.ORIGIN, Direction.DOWN)) + player.motion = Vec3d.ZERO + + player.fallDistance = 0f + } + } + } + } + +} \ No newline at end of file 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 3cc3e2dc4..ea49516a9 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 @@ -13,9 +13,11 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.Nameable import com.lambda.util.player.MovementUtils.addSpeed +import com.lambda.util.player.MovementUtils.buildMovementInput import com.lambda.util.player.MovementUtils.calcMoveYaw import com.lambda.util.player.MovementUtils.handledByBaritone import com.lambda.util.player.MovementUtils.isInputting +import com.lambda.util.player.MovementUtils.mergeFrom import com.lambda.util.player.MovementUtils.motionY import com.lambda.util.player.MovementUtils.moveDelta import com.lambda.util.player.MovementUtils.newMovementInput @@ -25,6 +27,7 @@ import com.lambda.util.player.MovementUtils.setSpeed import com.lambda.util.primitives.extension.contains import com.lambda.util.world.WorldUtils.getFastEntities import net.minecraft.entity.LivingEntity +import net.minecraft.entity.decoration.ArmorStandEntity import net.minecraft.entity.vehicle.BoatEntity object Speed : Module( @@ -42,6 +45,7 @@ object Speed : Module( private val grimEntityBoost by setting("Entity Boost", 1.0, 0.0..2.0, 0.01) { mode == Mode.GRIM_STRAFE } private val grimCollideMultiplier by setting("Entity Collide Multiplier", 0.5, 0.0..1.0, 0.01) { mode == Mode.GRIM_STRAFE && grimEntityBoost > 0.0} private val grimBoatBoost by setting("Boat Boost", 0.4, 0.0..1.0, 0.01) { mode == Mode.GRIM_STRAFE } + private val grimMaxSpeed by setting("Max Speed", 1.0, 0.2..1.0, 0.01) { mode == Mode.GRIM_STRAFE } // NCP private val strict by setting("Strict", true) { mode == Mode.NCP_STRAFE } @@ -55,7 +59,7 @@ object Speed : Module( } // NCP - private const val NCP_BASE_SPEED = 0.2873 + const val NCP_BASE_SPEED = 0.2873 private const val NCP_AIR_DECAY = 0.9937 private var ncpPhase = NCPPhase.SLOWDOWN @@ -110,7 +114,7 @@ object Speed : Module( listener { event -> if (mode != Mode.GRIM_STRAFE) return@listener if (!shouldWork() || !isInputting) return@listener - if (player.input.handledByBaritone) return@listener + if (player.input.handledByBaritone || TargetStrafe.isActive) return@listener val input = newMovementInput() val yaw = calcMoveYaw(player.yaw, input.roundedForward, input.roundedStrafing) @@ -126,12 +130,13 @@ object Speed : Module( private fun SafeContext.handleGrim() { if (!isInputting) return + if (player.moveDelta > grimMaxSpeed) return var boostAmount = 0.0 getFastEntities( player.pos, 3.0, - predicate = { player.boundingBox.expand(1.0) in it.boundingBox }, + predicate = { player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity }, iterator = { e, _ -> val colliding = player.boundingBox in e.boundingBox val multiplier = if (colliding) grimCollideMultiplier else 1.0 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 new file mode 100644 index 000000000..c8bbc9329 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt @@ -0,0 +1,84 @@ +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.listener +import com.lambda.interaction.rotation.Rotation.Companion.rotationTo +import com.lambda.module.Module +import com.lambda.module.modules.combat.KillAura +import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.player.MovementUtils.buildMovementInput +import com.lambda.util.player.MovementUtils.mergeFrom +import kotlin.math.pow + +object TargetStrafe : Module( + name = "TargetStrafe", + description = "Automatically strafes around entities", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + private val targetDistance by setting("Strafe Distance", 1.0, 0.0..5.0, 0.1) + private val jitterCompensation by setting("Jitter Compensation", 0.0, 0.0..1.0, 0.1) + private val stabilize by setting("Stabilize", StabilizationMode.NORMAL) + + enum class StabilizationMode { + NONE, WEAK, NORMAL, STRONG + } + + private var forwardDirection = 1 + private var strafeDirection = 1 + + @JvmStatic val isActive get() = isEnabled && KillAura.isEnabled && KillAura.target != null + + init { + listener { + if (player.horizontalCollision) strafeDirection *= -1 + + if (KillAura.target == null) { + forwardDirection = 1 + strafeDirection = 1 + } + } + + listener { event -> + KillAura.target?.let { target -> + event.strafeYaw = player.eyePos.rotationTo(target.boundingBox.center).yaw + + val distSq = player.pos distSq target.pos + val keepRange = 0.5 * jitterCompensation + + forwardDirection = when { + distSq > (targetDistance + keepRange).pow(2) -> 1 + distSq < (targetDistance - keepRange).pow(2) -> -1 + else -> forwardDirection + } + + // Premium code, do not touch it bites + var shouldStabilize = when (stabilize) { + StabilizationMode.NONE -> false + StabilizationMode.WEAK -> player.age % 4 == 0 // 1/4 + StabilizationMode.NORMAL -> player.age % 2 == 0 // 2/4 + StabilizationMode.STRONG -> player.age % 4 != 0 // 3/4 + } + + shouldStabilize = shouldStabilize && distSq > (targetDistance + 0.5).pow(2) + + var strafe = strafeDirection.toDouble() + if (shouldStabilize) strafe = 0.0 + + event.input.mergeFrom( + buildMovementInput( + forwardDirection.toDouble(), + strafe, + true + ) + ) + } + } + + onEnable { + forwardDirection = 1 + strafeDirection = 1 + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/TickShift.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/TickShift.kt new file mode 100644 index 000000000..1f9d92f1c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/TickShift.kt @@ -0,0 +1,138 @@ +package com.lambda.module.modules.movement + +import com.lambda.context.SafeContext +import com.lambda.event.events.ClientEvent +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.modules.combat.KillAura +import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runConcurrent +import com.lambda.util.Communication.info +import com.lambda.util.PacketUtils.handlePacketSilently +import com.lambda.util.PacketUtils.sendPacketSilently +import kotlinx.coroutines.delay +import net.minecraft.network.packet.c2s.common.CommonPongC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket +import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket + +object TickShift : Module( + name = "TickShift", + description = "Smort tickshift for smort anticheats", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + val maxBalance by setting("Max Balance", 20, 3..400, 1) + private val boostAmount by setting("Boost", 3.0, 1.1..20.0, 0.01) + private val slowdown by setting("Slowdown", 0.35, 0.01..0.9, 0.01) + private val delaySetting by setting("Delay", 0, 0..2000, 10) + private val strict by setting("Strict", true) + private val shiftVelocity by setting("Shift velocity", true) + private val requiresAura by setting("Requires Aura", false) + + val isActive: Boolean get() { + if (requiresAura && (!KillAura.isEnabled || KillAura.target == null)) return false + return System.currentTimeMillis() - lastBoost > delaySetting + } + + private var pingPool = ArrayDeque() + private var lastVelocity: EntityVelocityUpdateS2CPacket? = null + + var balance = 0 + var boost = false + private var lastBoost = 0L + + init { + listener { + if (Blink.isEnabled) { + this@TickShift.info("TickShift is incompatible with blink") + disable() + } + + if (strict) balance-- + + if (balance <= 0) { + balance = 0 + poolPackets() + + if (boost) { + boost = false + lastBoost = System.currentTimeMillis() + } + } + } + + listener { + if (it.packet !is PlayerMoveC2SPacket) return@listener + if (!strict) balance-- + } + + runConcurrent { + while (true) { + delay(50) + + if (isEnabled) { + if (++balance >= maxBalance) { + balance = maxBalance + boost = isActive + } + } + } + } + + listener { + if (!isActive) { + poolPackets() + return@listener + } + + it.speed = if (boost) boostAmount else slowdown + } + + listener { event -> + if (!isActive) return@listener + if (event.packet !is CommonPongC2SPacket) return@listener + + pingPool.add(event.packet) + event.cancel() + return@listener + } + + listener { event -> + if (!isActive || !shiftVelocity) return@listener + + if (event.packet !is EntityVelocityUpdateS2CPacket) return@listener + if (event.packet.id != player.id) return@listener + + lastVelocity = event.packet + event.cancel() + return@listener + } + + onEnable { + balance = 0 + boost = false + + pingPool.clear() + lastVelocity = null + } + + onDisable { + balance = 0 + boost = false + + poolPackets() + } + } + + private fun SafeContext.poolPackets() { + while (pingPool.isNotEmpty()) { + connection.sendPacketSilently(pingPool.removeFirst()) + } + + lastVelocity?.let { + connection.handlePacketSilently(it) + lastVelocity = null + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/network/PacketDelay.kt b/common/src/main/kotlin/com/lambda/module/modules/network/PacketDelay.kt index 4cb48fbce..3f2ecced2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/network/PacketDelay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/network/PacketDelay.kt @@ -1,66 +1,120 @@ package com.lambda.module.modules.network +import com.lambda.context.SafeContext import com.lambda.event.events.PacketEvent +import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runConcurrent import com.lambda.threading.runGameScheduled +import com.lambda.util.PacketUtils.handlePacketSilently +import com.lambda.util.PacketUtils.sendPacketSilently import kotlinx.coroutines.delay -import net.minecraft.network.ClientConnection +import net.minecraft.network.listener.ClientPacketListener +import net.minecraft.network.listener.ServerPacketListener import net.minecraft.network.packet.Packet import net.minecraft.network.packet.c2s.common.KeepAliveC2SPacket +import java.util.concurrent.ConcurrentLinkedDeque object PacketDelay : Module( name = "PacketDelay", description = "Delays packets client-bound & server-bound", defaultTags = setOf(ModuleTag.NETWORK), ) { + private val mode by setting("Mode", Mode.STATIC) private val networkScope by setting("Network Scope", Direction.BOTH) private val packetScope by setting("Packet Scope", PacketType.ANY) private val inboundDelay by setting("Inbound Delay", 250L, 0L..5000L, 10L, unit = "ms") { networkScope != Direction.OUTBOUND } private val outboundDelay by setting("Outbound Delay", 250L, 0L..5000L, 10L, unit = "ms") { networkScope != Direction.INBOUND } - enum class Direction { - BOTH, - INBOUND, - OUTBOUND - } - - enum class PacketType(val filter: (Packet<*>) -> Boolean) { - ANY({ true }), - KEEP_ALIVE({ it is KeepAliveC2SPacket }) - } + private var outboundPool = ConcurrentLinkedDeque>() + private var inboundPool = ConcurrentLinkedDeque>() + private var outboundLastUpdate = 0L + private var inboundLastUpdate = 0L init { - listener(Int.MIN_VALUE) { event -> - if (!connection.connection.isOpen) return@listener + listener { + if (mode != Mode.STATIC) return@listener + + flushPools(System.currentTimeMillis()) + } + + listener(Int.MIN_VALUE) { event -> if (!packetScope.filter(event.packet)) return@listener - event.cancel() - runConcurrent { - delay(inboundDelay) - runGameScheduled { - if (connection.connection.packetListener?.accepts(event.packet) == false) return@runGameScheduled + when (mode) { + Mode.STATIC -> { + outboundPool.add(event.packet) + event.cancel() + } - ClientConnection.handlePacket(event.packet, connection.connection.packetListener) - connection.connection.packetsReceivedCounter++ + Mode.PULSE -> { + runConcurrent { + delay(outboundDelay) + runGameScheduled { + connection.sendPacketSilently(event.packet) + } + } + event.cancel() } } } - listener(Int.MIN_VALUE) { event -> - if (!connection.connection.isOpen) return@listener + listener(Int.MIN_VALUE) { event -> if (!packetScope.filter(event.packet)) return@listener + + when (mode) { + Mode.STATIC -> { + inboundPool.add(event.packet) + event.cancel() + } + + Mode.PULSE -> { + runConcurrent { + delay(inboundDelay) + runGameScheduled { + connection.handlePacketSilently(event.packet) + } + } + event.cancel() + } + } + event.cancel() + } + + onDisable { + flushPools(System.currentTimeMillis()) + } + } + + private fun SafeContext.flushPools(time: Long) { + if (time - outboundLastUpdate >= outboundDelay) { + while (outboundPool.isNotEmpty()) { + outboundPool.poll().let { packet -> + connection.sendPacketSilently(packet) + } + } + + outboundLastUpdate = time + } - runConcurrent { - delay(outboundDelay) - runGameScheduled { - connection.connection.send(event.packet, null) - connection.connection.packetsSentCounter++ + if (time - inboundLastUpdate >= inboundDelay) { + while (inboundPool.isNotEmpty()) { + inboundPool.poll().let { packet -> + connection.handlePacketSilently(packet) } } + + inboundLastUpdate = time } } + + enum class Mode { STATIC, PULSE, } + enum class Direction { BOTH, INBOUND, OUTBOUND } + enum class PacketType(val filter: (Packet<*>) -> Boolean) { + ANY({ true }), + KEEP_ALIVE({ it is KeepAliveC2SPacket }) + } } 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 62ea5d341..a25bba4ec 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 @@ -548,7 +548,7 @@ object Replay : Module( val size: Int get() = minOf(input.size, rotation.size, sprint.size, position.size) val duration: Duration - get() = (size * TimerManager.tickLength * 1.0).toDuration(DurationUnit.MILLISECONDS) + get() = (size * TimerManager.lastTickLength * 1.0).toDuration(DurationUnit.MILLISECONDS) val startPos: Vec3d get() = position.firstOrNull() ?: Vec3d.ZERO val endPos: Vec3d 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 new file mode 100644 index 000000000..7e23e5d65 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt @@ -0,0 +1,197 @@ +package com.lambda.module.modules.render + +import com.lambda.Lambda.mc +import com.lambda.context.SafeContext +import com.lambda.event.events.AttackEvent +import com.lambda.event.events.MovementEvent +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.gl.GlStateUtils.withBlendFunc +import com.lambda.graphics.gl.GlStateUtils.withDepth +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.module.Module +import com.lambda.module.modules.client.GuiSettings +import com.lambda.module.modules.client.GuiSettings.colorSpeed +import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.MathUtils.random +import com.lambda.util.math.VecUtils +import com.lambda.util.math.VecUtils.plus +import com.lambda.util.math.VecUtils.times +import com.lambda.util.math.transform +import com.lambda.util.player.MovementUtils.moveDelta +import com.lambda.util.primitives.extension.partialTicks +import com.lambda.util.world.raycast.RayCastMask +import net.minecraft.entity.Entity +import net.minecraft.util.math.Vec3d + +import org.lwjgl.opengl.GL11.GL_ONE +import org.lwjgl.opengl.GL11.GL_SRC_ALPHA +import kotlin.math.sin + +object Particles : Module( + name = "Particles", + description = "Spawns fancy particles", + defaultTags = setOf(ModuleTag.RENDER) +) { + // ToDo: resort, cleanup settings + private val duration by setting("Duration", 5.0, 1.0..500.0, 1.0) + private val fadeDuration by setting("Fade Ticks", 5.0, 1.0..30.0, 1.0) + private val spawnAmount by setting("Spawn Amount", 20, 3..500, 1) + private val sizeSetting by setting("Size", 2.0, 0.1..50.0, 0.1) + private val alphaSetting by setting("Alpha", 1.5, 0.01..2.0, 0.01) + private val speedH by setting("Speed H", 1.0, 0.0..10.0, 0.1) + private val speedV by setting("Speed V", 1.0, 0.0..10.0, 0.1) + private val inertia by setting("Inertia", 0.0, 0.0..1.0, 0.01) + private val gravity by setting("Gravity", 0.2, 0.0..1.0, 0.01) + private val onMove by setting("On Move", false) + + private val environment by setting("Environment", true) + private val environmentSpawnAmount by setting("E Spawn Amount", 10, 3..100, 1) { environment } + private val environmentSize by setting("E Size", 2.0, 0.1..50.0, 0.1) { environment } + private val environmentRange by setting("E Spread", 5.0, 1.0..20.0, 0.1) { environment } + private val environmentSpeedH by setting("E Speed H", 0.0, 0.0..10.0, 0.1) { environment } + private val environmentSpeedV by setting("E Speed V", 0.1, 0.0..10.0, 0.1) { environment } + + private var particles = mutableListOf() + private val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.PARTICLE) + private val shader = Shader("renderer/particle", "renderer/particle") + + init { + listener { + if (environment) spawnForEnvironment() + particles.removeIf(Particle::update) + } + + listener { + // Todo: interpolated tickbased upload? + particles.forEach(Particle::build) + + withBlendFunc(GL_SRC_ALPHA, GL_ONE) { + shader.use() + shader["u_CameraPosition"] = mc.gameRenderer.camera.pos + + vao.upload() + withDepth(vao::render) + vao.clear() + } + } + + listener { event -> + spawnForEntity(event.entity) + } + + listener { + if (!onMove || player.moveDelta < 0.05) return@listener + spawnForEntity(player) + } + } + + private fun spawnForEntity(entity: Entity) { + repeat(spawnAmount) { + val i = (it + 1) / spawnAmount.toDouble() + + val pos = entity.pos + val height = entity.boundingBox.lengthY + val spawnHeight = height * transform(i, 0.0, 1.0, 0.2, 0.8) + val particlePos = pos.add(0.0, spawnHeight, 0.0) + val particleMotion = Rotation( + random(-180.0, 180.0), + random(-90.0, 90.0) + ).vector * Vec3d(speedH, speedV, speedH) * 0.1 + + particles += Particle(particlePos, particleMotion, false) + } + } + + private fun SafeContext.spawnForEnvironment() { + if (mc.paused) return + 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 + VecUtils.UP * 2.0, true, RayCastMask.BLOCK)?.pos?.let { + particlePos = it + VecUtils.UP * 0.03 + } ?: return@repeat + + val particleMotion = Rotation( + random(-180.0, 180.0), + random(-90.0, 90.0) + ).vector * Vec3d(environmentSpeedH, environmentSpeedV, environmentSpeedH) * 0.1 + + particles += Particle(particlePos, particleMotion, true) + } + } + + private class Particle( + initialPosition: Vec3d, + initialMotion: Vec3d, + val lay: Boolean + ) { + private val fadeTicks = fadeDuration + + private var age = 0 + private val maxAge = (duration + random(0.0, 20.0)).toInt() + + private var prevPos = initialPosition + private var position = initialPosition + private var motion = initialMotion + + private val projRotation = if (lay) Matrices.ProjRotationMode.UP else Matrices.ProjRotationMode.TO_CAMERA + + fun update(): Boolean { + if (mc.paused) return false + age++ + + prevPos = position + + if (!lay) motion += VecUtils.DOWN * gravity * 0.01 + motion *= 0.9 + inertia * 0.1 + + position += motion + + return age > maxAge + fadeTicks * 2 + 5 + } + + fun build() { + val smoothAge = age + mc.partialTicks + val colorTicks = smoothAge * 0.1 / colorSpeed + + val alpha = when { + smoothAge < fadeTicks -> smoothAge / fadeTicks + smoothAge in fadeTicks..fadeTicks + maxAge -> 1.0 + else -> { + val min = fadeTicks + maxAge + val max = fadeTicks * 2 + maxAge + transform(smoothAge, min, max, 1.0, 0.0) + } + } + + val (c1, c2) = GuiSettings.primaryColor to GuiSettings.secondaryColor + val color = lerp(c1, c2, sin(colorTicks) * 0.5 + 0.5).multAlpha(alpha * alphaSetting) + + val position = lerp(prevPos, position, mc.partialTicks) + val size = if (lay) environmentSize else sizeSetting * lerp(0.5, 1.0, alpha) + + withVertexTransform(buildWorldProjection(position, size, projRotation)) { + vao.use { + grow(4) // DO NOT FUCKING FORGOTEOIJTOWKET TO GROW (cost me an hour) + putQuad( + vec3m(-1.0, -1.0, 0.0).vec2(0.0, 0.0).color(color).end(), + vec3m(-1.0, 1.0, 0.0).vec2(0.0, 1.0).color(color).end(), + vec3m(1.0, 1.0, 0.0).vec2(1.0, 1.0).color(color).end(), + vec3m(1.0, -1.0, 0.0).vec2(1.0, 0.0).color(color).end() + ) + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/PacketUtils.kt b/common/src/main/kotlin/com/lambda/util/PacketUtils.kt new file mode 100644 index 000000000..994bc1a0b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/PacketUtils.kt @@ -0,0 +1,44 @@ +package com.lambda.util + +import net.minecraft.client.network.ClientPlayNetworkHandler +import net.minecraft.network.ClientConnection +import net.minecraft.network.listener.ClientPacketListener +import net.minecraft.network.listener.ServerPacketListener +import net.minecraft.network.packet.Packet + +object PacketUtils { + /** + * Sends a packet to the server without notifying the client. + * It bypasses the mixins that would normally intercept the packet + * and send it through the client's event bus. + * + * @param packet The packet to send. + */ + fun ClientPlayNetworkHandler.sendPacketSilently(packet: ClientPacket) { + if (!connection.isOpen) return + if (connection.packetListener?.accepts(packet) == true) + return // LOG.debug("Client tried to send client-bound packet {} to server ", packet) + + connection.send(packet, null, true) + connection.packetsSentCounter++ + } + + /** + * Handles a packet without notifying the client. + * It bypasses the mixins that would normally intercept the packet + * and send it through the client's event bus. + * + * @param packet The packet to handle. + */ + fun ClientPlayNetworkHandler.handlePacketSilently(packet: ServerPacket) { + if (!connection.isOpen) return + if (connection.packetListener?.accepts(packet) == false) + return // LOG.debug("Client tried to handle server-bound packet {}", packet) + + ClientConnection.handlePacket(packet, connection.packetListener) + connection.packetsReceivedCounter++ + } +} + +typealias ClientPacket = Packet +typealias ServerPacket = Packet diff --git a/common/src/main/kotlin/com/lambda/util/SelfReference.kt b/common/src/main/kotlin/com/lambda/util/SelfReference.kt new file mode 100644 index 000000000..2392316bd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/SelfReference.kt @@ -0,0 +1,10 @@ +package com.lambda.util + +class SelfReference(initializer: SelfReference.() -> T) { + val self: T by lazy { inner } + + private val inner = initializer() + operator fun getValue(thisRef: Any?, property: Any?) = self +} + +fun selfReference(initializer: SelfReference.() -> T): SelfReference = SelfReference(initializer) 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 8ef87927b..24ca4e575 100644 --- a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt @@ -1,10 +1,13 @@ package com.lambda.util.math import com.lambda.interaction.rotation.Rotation +import com.lambda.module.modules.client.ClickGui import com.lambda.util.math.ColorUtils.a import com.lambda.util.math.ColorUtils.b import com.lambda.util.math.ColorUtils.g import com.lambda.util.math.ColorUtils.r +import com.lambda.util.math.MathUtils.roundToStep +import net.fabricmc.loader.impl.lib.sat4j.core.Vec import net.minecraft.util.math.Vec3d import java.awt.Color import java.math.BigDecimal @@ -33,6 +36,8 @@ object MathUtils { fun T.roundToStep(step: T): T { val stepD = step.toDouble() + if (stepD == 0.0) return this + var value = round(toDouble() / stepD) * stepD value = value.roundToPlaces(decimalPlaces(stepD)) if (abs(value) == 0.0) value = 0.0 @@ -40,6 +45,9 @@ object MathUtils { return typeConvert(value) } + fun Vec2d.roundToStep(step: Double): Vec2d = + Vec2d(x.roundToStep(step), y.roundToStep(step)) + fun Double.roundToPlaces(places: Int) = BigDecimal(this).setScale(places, RoundingMode.HALF_EVEN).toDouble() diff --git a/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt b/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt index 966d59c95..6da6ef000 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt @@ -1,5 +1,7 @@ package com.lambda.util.math +import kotlin.math.roundToInt + data class Vec2d(val x: Double, val y: Double) { constructor(x: Int, y: Int) : this(x.toDouble(), y.toDouble()) constructor(x: Float, y: Float) : this(x.toDouble(), y.toDouble()) @@ -22,13 +24,15 @@ data class Vec2d(val x: Double, val y: Double) { operator fun div(divider: Double) = div(divider, divider) fun div(x: Double, y: Double) = Vec2d(this.x / x, this.y / y) + fun roundToInt(): Vec2d = Vec2d(this.x.roundToInt(), this.y.roundToInt()) + companion object { val ZERO = Vec2d(0.0, 0.0) val ONE = Vec2d(1.0, 1.0) val LEFT = Vec2d(-1.0, 0.0) val RIGHT = Vec2d(1.0, 0.0) - val TOP = Vec2d(0.0, 1.0) - val BOTTOM = Vec2d(0.0, -1.0) + val TOP = Vec2d(0.0, -1.0) + val BOTTOM = Vec2d(0.0, 1.0) } } 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 6f1b4d072..21d3626fc 100644 --- a/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt @@ -32,4 +32,7 @@ object VecUtils { infix operator fun Vec3d.times(other: Double): Vec3d = this.multiply(other) infix operator fun Vec3d.div(other: Double): Vec3d = this.multiply(1.0 / other) + + val UP = Vec3d(0.0, 1.0, 0.0) + val DOWN = Vec3d(0.0, -1.0, 0.0) } 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 28a218ad9..7186654bc 100644 --- a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt @@ -41,6 +41,37 @@ object MovementUtils { } } + fun buildMovementInput( + forward: Double, + strafe: Double, + jump: Boolean = false, + sneak: Boolean = false + ) = Input().apply { + movementForward = forward.toFloat() + movementSideways = strafe.toFloat() + + pressingForward = forward > 0.0 + pressingBack = forward < 0.0 + pressingLeft = strafe < 0.0 + pressingRight = strafe > 0.0 + + jumping = jump + sneaking = sneak + } + + fun Input.mergeFrom(input: Input) { + movementForward = input.movementForward + movementSideways = input.movementSideways + + pressingForward = input.pressingForward + pressingBack = input.pressingBack + pressingLeft = input.pressingLeft + pressingRight = input.pressingRight + + jumping = input.jumping + sneaking = input.sneaking + } + fun Input.cancel(cancelVertical: Boolean = true) { movementForward = 0f movementSideways = 0f diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/particle.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/particle.frag new file mode 100644 index 000000000..53b683d85 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/particle.frag @@ -0,0 +1,11 @@ +#version 330 core + +in vec2 v_TexCoord; +in vec4 v_Color; + +out vec4 color; + +void main() { + float a = 1.0 - length(v_TexCoord - 0.5) * 2.0; + color = v_Color * vec4(1.0, 1.0, 1.0, a); +} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/particle.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/particle.vert new file mode 100644 index 000000000..6e3fed3d9 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/particle.vert @@ -0,0 +1,19 @@ +#version 330 core + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec4 color; + +uniform mat4 u_ProjModel; +uniform vec3 u_CameraPosition; + +out vec2 v_TexCoord; +out vec4 v_Color; + +#define VERTEX_POSITION pos - u_CameraPosition + +void main() { + gl_Position = u_ProjModel * vec4(VERTEX_POSITION, 1.0); + v_TexCoord = uv; + v_Color = color; +} diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 4a8c265d5..bd4ab6ec7 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -18,6 +18,7 @@ accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Ln 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 accessible method net/minecraft/entity/passive/AbstractHorseEntity updateSaddle ()V +accessible field net/minecraft/entity/LivingEntity lastAttackedTicks I # Camera accessible method net/minecraft/client/render/Camera setPos (DDD)V @@ -46,6 +47,7 @@ accessible field net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPa 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 +accessible field net/minecraft/network/packet/s2c/play/EntityS2CPacket id I accessible method net/minecraft/network/ClientConnection handlePacket (Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;)V accessible field net/minecraft/network/ClientConnection packetsReceivedCounter I accessible field net/minecraft/network/ClientConnection packetsSentCounter I diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index 4a8f53c42..ff8a0378b 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -33,6 +33,7 @@ "render.ScreenHandlerMixin", "render.VertexBufferMixin", "render.WorldRendererMixin", + "world.BlockCollisionSpliteratorMixin", "world.ClientChunkManagerMixin", "world.ClientWorldMixin" ],