Skip to content

Commit ebef165

Browse files
committed
Move freecam module
1 parent e80368d commit ebef165

1 file changed

Lines changed: 196 additions & 0 deletions

File tree

  • src/main/kotlin/com/lambda/module/modules/render
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
* Copyright 2026 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.module.modules.render
19+
20+
import com.lambda.Lambda
21+
import com.lambda.event.events.MovementEvent
22+
import com.lambda.event.events.PlayerEvent
23+
import com.lambda.event.events.RenderEvent
24+
import com.lambda.event.events.TickEvent
25+
import com.lambda.event.listener.SafeListener.Companion.listen
26+
import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest
27+
import com.lambda.interaction.managers.rotating.Rotation
28+
import com.lambda.interaction.managers.rotating.RotationConfig
29+
import com.lambda.interaction.managers.rotating.RotationMode
30+
import com.lambda.interaction.managers.rotating.visibilty.lookAt
31+
import com.lambda.module.Module
32+
import com.lambda.module.tag.ModuleTag
33+
import com.lambda.threading.runSafeAutomated
34+
import com.lambda.util.Communication.info
35+
import com.lambda.util.Describable
36+
import com.lambda.util.NamedEnum
37+
import com.lambda.util.extension.rotation
38+
import com.lambda.util.math.interpolate
39+
import com.lambda.util.math.plus
40+
import com.lambda.util.math.times
41+
import com.lambda.util.player.MovementUtils.calcMoveRad
42+
import com.lambda.util.player.MovementUtils.cancel
43+
import com.lambda.util.player.MovementUtils.handledByBaritone
44+
import com.lambda.util.player.MovementUtils.isInputting
45+
import com.lambda.util.player.MovementUtils.movementVector
46+
import com.lambda.util.player.MovementUtils.newMovementInput
47+
import com.lambda.util.player.MovementUtils.roundedForward
48+
import com.lambda.util.player.MovementUtils.roundedStrafing
49+
import com.lambda.util.player.MovementUtils.verticalMovement
50+
import com.lambda.util.world.raycast.RayCastUtils.orMiss
51+
import net.minecraft.client.option.Perspective
52+
import net.minecraft.util.hit.BlockHitResult
53+
import net.minecraft.util.hit.HitResult
54+
import net.minecraft.util.math.BlockPos
55+
import net.minecraft.util.math.Direction
56+
import net.minecraft.util.math.Vec3d
57+
58+
object Freecam : Module(
59+
name = "Freecam",
60+
description = "Move your camera freely",
61+
tag = ModuleTag.RENDER,
62+
autoDisable = true,
63+
) {
64+
private val speed by setting("Speed", 0.5, 0.1..1.0, 0.1, "Freecam movement speed", unit = "m/s")
65+
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.")
66+
private val reach by setting("Reach", 10.0, 1.0..100.0, 1.0, "Freecam reach distance")
67+
private val rotateMode by setting("Rotate Mode", FreecamRotationMode.None, "Rotation mode")
68+
.onValueChange { _, it -> if (it == FreecamRotationMode.LookAtTarget) mc.crosshairTarget = BlockHitResult.createMissed(Vec3d.ZERO, Direction.UP, BlockPos.ORIGIN) }
69+
private val relative by setting("Relative", false, "Moves freecam relative to player position")
70+
.onValueChange { _, it -> if (it) lastPlayerPosition = player.pos }
71+
private val keepYLevel by setting("Keep Y Level", false, "Don't change the camera y-level on player movement") { relative }
72+
73+
override val rotationConfig = RotationConfig.Instant(RotationMode.Lock)
74+
75+
private var lastPerspective = Perspective.FIRST_PERSON
76+
private var lastPlayerPosition: Vec3d = Vec3d.ZERO
77+
private var prevPosition: Vec3d = Vec3d.ZERO
78+
private var position: Vec3d = Vec3d.ZERO
79+
private val lerpPos: Vec3d
80+
get() {
81+
val tickProgress = Lambda.mc.gameRenderer.camera.lastTickProgress
82+
return prevPosition.interpolate(tickProgress, position)
83+
}
84+
85+
private var rotation: Rotation = Rotation.Companion.ZERO
86+
private var velocity: Vec3d = Vec3d.ZERO
87+
private var loading = false
88+
89+
@JvmStatic
90+
fun updateCam() {
91+
Lambda.mc.gameRenderer.apply {
92+
camera.setRotation(rotation.yawF, rotation.pitchF)
93+
camera.setPos(lerpPos.x, lerpPos.y, lerpPos.z)
94+
}
95+
}
96+
97+
/**
98+
* @see net.minecraft.entity.Entity.changeLookDirection
99+
*/
100+
private const val SENSITIVITY_FACTOR = 0.15
101+
102+
init {
103+
onEnable {
104+
lastPerspective = mc.options.perspective
105+
position = player.eyePos
106+
rotation = player.rotation
107+
velocity = Vec3d.ZERO
108+
lastPlayerPosition = player.pos
109+
}
110+
111+
onDisable {
112+
mc.options.perspective = lastPerspective
113+
}
114+
115+
listen<TickEvent.Pre> {
116+
when (rotateMode) {
117+
FreecamRotationMode.None -> return@listen
118+
FreecamRotationMode.KeepRotation -> rotationRequest { rotation(rotation) }.submit()
119+
FreecamRotationMode.LookAtTarget ->
120+
mc.crosshairTarget?.let {
121+
runSafeAutomated {
122+
rotationRequest { rotation(lookAt(it.pos)) }.submit()
123+
}
124+
}
125+
}
126+
}
127+
128+
listen<PlayerEvent.World.Respawn> {
129+
loading = true
130+
info("Respawned, waiting for position look packet to update freecam position...")
131+
}
132+
133+
listen<PlayerEvent.World.SetPosition> {
134+
info("Received position look packet, updating freecam position")
135+
info("New position: ${it.position}")
136+
if (loading) {
137+
loading = false
138+
position = player.eyePos
139+
rotation = player.rotation
140+
}
141+
}
142+
143+
listen<PlayerEvent.ChangeLookDirection> {
144+
rotation = rotation.withDelta(
145+
it.deltaYaw * SENSITIVITY_FACTOR,
146+
it.deltaPitch * SENSITIVITY_FACTOR
147+
)
148+
it.cancel()
149+
}
150+
151+
listen<MovementEvent.InputUpdate> { event ->
152+
mc.options.perspective = Perspective.FIRST_PERSON
153+
154+
// Don't block baritone from working
155+
if (!event.input.handledByBaritone) {
156+
// Reset actual input
157+
event.input.cancel()
158+
}
159+
160+
// Create new input for freecam
161+
val input = newMovementInput(assumeBaritone = false, slowdownCheck = false)
162+
val sprintModifier = if (mc.options.sprintKey.isPressed) sprint else 1.0
163+
val moveDir = calcMoveRad(rotation.yawF, input.roundedForward, input.roundedStrafing)
164+
var moveVec = movementVector(moveDir, input.verticalMovement) * speed * sprintModifier
165+
if (!input.isInputting) moveVec *= Vec3d(0.0, 1.0, 0.0)
166+
167+
// Apply movement
168+
velocity + moveVec
169+
velocity *= 0.6
170+
171+
// Update position
172+
prevPosition = position
173+
position += velocity
174+
175+
if (relative) {
176+
val delta = player.pos.subtract(lastPlayerPosition)
177+
position += if (keepYLevel) Vec3d(delta.x, 0.0, delta.z) else delta
178+
lastPlayerPosition = player.pos
179+
}
180+
}
181+
182+
listen<RenderEvent.UpdateTarget>({ 1 }) { event -> // Higher priority then RotationManager to run before RotationManager modifies mc.crosshairTarget
183+
mc.crosshairTarget = rotation
184+
.rayCast(reach, lerpPos)
185+
.orMiss // Can't be null (otherwise mc will spam "Null returned as 'hitResult', this shouldn't happen!")
186+
187+
mc.crosshairTarget?.let { if (it.type != HitResult.Type.MISS) event.cancel() }
188+
}
189+
}
190+
191+
enum class FreecamRotationMode(override val displayName: String, override val description: String) : NamedEnum, Describable {
192+
None("None", "No rotation changes"),
193+
LookAtTarget("Look At Target", "Look at the block or entity under your crosshair"),
194+
KeepRotation("Keep Rotation", "Look in the same direction as the camera");
195+
}
196+
}

0 commit comments

Comments
 (0)