diff --git a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java index 8107b5e38..acb49eeb6 100644 --- a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java @@ -21,6 +21,7 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.ChatEvent; import com.lambda.event.events.InventoryEvent; +import com.lambda.event.events.PlayerEvent; import com.lambda.event.events.WorldEvent; import com.lambda.interaction.managers.inventory.InventoryManager; import com.lambda.module.modules.movement.Velocity; @@ -29,15 +30,21 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import net.minecraft.client.MinecraftClient; import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.PlayerListEntry; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityPosition; +import net.minecraft.entity.Entity; import net.minecraft.network.packet.s2c.play.*; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Set; @Mixin(ClientPlayNetworkHandler.class) public class ClientPlayNetworkHandlerMixin { @@ -130,6 +137,27 @@ void onSendMessage(String content, Operation original) { original.call(event.getMessage()); } + @Inject(method = "onPlayerRespawn", at = @At("TAIL")) + void onPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci) { + EventFlow.post(new PlayerEvent.World.Respawn(packet)); + } + + @Inject(method = "onPlayerPositionLook", at = @At("TAIL")) + void onPlayerPositionLook(PlayerPositionLookS2CPacket packet, CallbackInfo ci) { + EventFlow.post(new PlayerEvent.World.PositionLook(packet)); + } + + @Inject(method = "setPosition", at = @At("TAIL")) + private static void onSetPosition(EntityPosition pos, Set flags, Entity entity, boolean bl, + CallbackInfoReturnable cir) { + if (cir.getReturnValue() == false) { + var player = MinecraftClient.getInstance().player; + assert player != null; + var event = new PlayerEvent.World.SetPosition(player.getEntityPos(), player.getVelocity(), player.getYaw(), player.getPitch()); + EventFlow.post(event); + } + } + @Inject(method = "onOverlayMessage", at = @At("HEAD"), cancellable = true) void onOverlayMessage(OverlayMessageS2CPacket packet, CallbackInfo ci) { if (NoRender.getNo2b2tActionText() && packet.text().getString().equals("2b2t.org")) { diff --git a/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt b/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt index 6f4196f68..ca1f349b9 100644 --- a/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt +++ b/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt @@ -20,6 +20,8 @@ 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.network.packet.s2c.play.PlayerPositionLookS2CPacket +import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.SlotActionType import net.minecraft.util.Hand @@ -27,6 +29,7 @@ import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.EntityHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d /** * Represents various events that can be triggered by the player during gameplay. @@ -138,4 +141,21 @@ sealed class PlayerEvent { val action: SlotActionType, val screenHandler: ScreenHandler, ) : ICancellable by Cancellable() + + sealed class World { + data class Respawn( + val packet: PlayerRespawnS2CPacket + ) : Event + + data class PositionLook( + val packet: PlayerPositionLookS2CPacket + ) : Event + + data class SetPosition( + val position: Vec3d, + val velocity: Vec3d, + val yaw: Float, + val pitch: Float + ) : Event + } } diff --git a/src/main/kotlin/com/lambda/module/modules/render/Freecam.kt b/src/main/kotlin/com/lambda/module/modules/render/Freecam.kt index 959dd43ce..ec5ec2ead 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/Freecam.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/Freecam.kt @@ -17,6 +17,7 @@ package com.lambda.module.modules.render +import com.lambda.Lambda import com.lambda.Lambda.mc import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig import com.lambda.config.applyEdits @@ -28,11 +29,14 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest import com.lambda.interaction.managers.rotating.Rotation +import com.lambda.interaction.managers.rotating.RotationConfig import com.lambda.interaction.managers.rotating.RotationMode import com.lambda.interaction.managers.rotating.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe +import com.lambda.threading.runSafeAutomated +import com.lambda.util.Communication.info import com.lambda.util.Describable import com.lambda.util.NamedEnum import com.lambda.util.extension.rotation @@ -73,17 +77,20 @@ object Freecam : Module( autoDisable = true, ) { private val mode by setting("Mode", Mode.Free, "Freecam movement mode") - private val speed by setting("Speed", 0.5, 0.1..1.0, 0.1, "Freecam movement speed", unit = "m/s") { mode == Mode.Free } private val sprint by setting("Sprint Multiplier", 3.0, 0.1..10.0, 0.1, description = "Set below 1.0 to fly slower on sprint.") { mode == Mode.Free } + private val speed by setting("Speed", 0.5, 0.1..1.0, 0.1, "Freecam movement speed", unit = "m/s") private val reach by setting("Reach", 10.0, 1.0..100.0, 1.0, "Freecam reach distance") - private val rotateMode by setting("Rotate Mode", FreecamRotationMode.None, "Rotation mode").onValueChange { _, it -> if (it == FreecamRotationMode.LookAtTarget) mc.crosshairTarget = BlockHitResult.createMissed(Vec3d.ZERO, Direction.UP, BlockPos.ORIGIN) } - private val relative by setting("Relative", false, "Moves freecam relative to player position") { mode == Mode.Free }.onValueChange { _, it -> if (it) lastPlayerPosition = player.pos } - private val keepYLevel by setting("Keep Y Level", false, "Don't change the camera y-level on player movement") { mode == Mode.Free && relative } + private val rotateMode by setting("Rotate Mode", FreecamRotationMode.None, "Rotation mode") + .onValueChange { _, it -> if (it == FreecamRotationMode.LookAtTarget) mc.crosshairTarget = BlockHitResult.createMissed(Vec3d.ZERO, Direction.UP, BlockPos.ORIGIN) } + private val relative by setting("Relative", false, "Moves freecam relative to player position") + .onValueChange { _, it -> if (it) lastPlayerPosition = player.pos } + private val keepYLevel by setting("Keep Y Level", false, "Don't change the camera y-level on player movement") { relative } // Follow Player settings private val followMaxDistance by setting("String Length", 10.0, 2.0..50.0, 0.5, "Maximum distance before the string pulls the camera", unit = "m") { mode == Mode.FollowPlayer } private val followTrackPlayer by setting("Track Player", false, "Keeps looking at the followed player") { mode == Mode.FollowPlayer } + override val rotationConfig = RotationConfig.Instant(RotationMode.Lock) private var lastPerspective = Perspective.FIRST_PERSON private var lastPlayerPosition: Vec3d = Vec3d.ZERO @@ -91,12 +98,13 @@ object Freecam : Module( private var position: Vec3d = Vec3d.ZERO private val lerpPos: Vec3d get() { - val tickProgress = mc.gameRenderer.camera.lastTickProgress + val tickProgress = Lambda.mc.gameRenderer.camera.lastTickProgress return prevPosition.interpolate(tickProgress, position) } - private var rotation: Rotation = Rotation.ZERO + private var rotation: Rotation = Rotation.ZERO private var velocity: Vec3d = Vec3d.ZERO + private var loading = false @JvmStatic fun updateCam() { @@ -147,12 +155,35 @@ object Freecam : Module( when (rotateMode) { FreecamRotationMode.None -> return@listen FreecamRotationMode.KeepRotation -> rotationRequest { rotation(rotation) }.submit() - FreecamRotationMode.LookAtTarget -> mc.crosshairTarget?.let { rotationRequest { rotation(lookAt(it.pos)) }.submit() } + FreecamRotationMode.LookAtTarget -> + mc.crosshairTarget?.let { + runSafeAutomated { + rotationRequest { rotation(lookAt(it.pos)) }.submit() + } + } + } + } + + listen { + loading = true + info("Respawned, waiting for position look packet to update freecam position...") + } + + listen { + info("Received position look packet, updating freecam position") + info("New position: ${it.position}") + if (loading) { + loading = false + position = player.eyePos + rotation = player.rotation } } listen { - rotation = rotation.withDelta(it.deltaYaw * SENSITIVITY_FACTOR, it.deltaPitch * SENSITIVITY_FACTOR) + rotation = rotation.withDelta( + it.deltaYaw * SENSITIVITY_FACTOR, + it.deltaPitch * SENSITIVITY_FACTOR + ) it.cancel() } @@ -173,9 +204,11 @@ object Freecam : Module( val moveDir = calcMoveRad(rotation.yawF, input.roundedForward, input.roundedStrafing) var moveVec = movementVector(moveDir, input.verticalMovement) * speed * sprintModifier if (!input.isInputting) moveVec *= Vec3d(0.0, 1.0, 0.0) + // Apply movement velocity += moveVec velocity *= 0.6 + // Update position prevPosition = position position += velocity @@ -225,7 +258,10 @@ object Freecam : Module( } listen({ 1 }) { event -> // Higher priority then RotationManager to run before RotationManager modifies mc.crosshairTarget - mc.crosshairTarget = rotation.rayCast(reach, lerpPos).orMiss // Can't be null (otherwise mc will spam "Null returned as 'hitResult', this shouldn't happen!") + mc.crosshairTarget = rotation + .rayCast(reach, lerpPos) + .orMiss // Can't be null (otherwise mc will spam "Null returned as 'hitResult', this shouldn't happen!") + mc.crosshairTarget?.let { if (it.type != HitResult.Type.MISS) event.cancel() } } }