Skip to content

Instantly share code, notes, and snippets.

@Ensamisten
Last active June 7, 2026 12:29
Show Gist options
  • Select an option

  • Save Ensamisten/b3192020bdd9682dfcf3df6c4d4e1499 to your computer and use it in GitHub Desktop.

Select an option

Save Ensamisten/b3192020bdd9682dfcf3df6c4d4e1499 to your computer and use it in GitHub Desktop.
package io.github.ensamisten.client.mixin.interfaces;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Entity.class)
public interface EntityAccessor {
@Accessor("xo")
@Mutable
void setXo(double value);
@Accessor("yo")
@Mutable
void setYo(double value);
@Accessor("zo")
@Mutable
void setZo(double value);
@Accessor("yRotO")
@Mutable
void setYRotO(float value);
@Accessor("xRotO")
@Mutable
void setXRotO(float value);
@Accessor("noPhysics")
@Mutable
void setNoPhysics(boolean value);
@Accessor("noPhysics")
boolean isNoPhysics();
}
package io.github.ensamisten.client.module.movement;
import io.github.ensamisten.client.event.EventRegistry;
import io.github.ensamisten.client.entity.FakePlayerEntity;
import io.github.ensamisten.client.event.impl.pkt.PacketEvent;
import io.github.ensamisten.client.mixin.interfaces.EntityAccessor;
import io.github.ensamisten.client.module.Extension;
import io.github.ensamisten.client.module.ModuleEntrypoint;
import io.github.ensamisten.client.setting.Setting;
import io.github.ensamisten.client.setting.SettingFactory;
import net.minecraft.client.Camera;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import java.util.function.Consumer;
public class Freecam extends Extension implements ModuleEntrypoint {
private final Minecraft mc = Minecraft.getInstance();
private final Setting<Float> flySpeed = SettingFactory.floatSetting(
"freecam_speed",
"Speed",
8.0f
);
private final Consumer<PacketEvent> packetListener = this::onPacket;
private Player lockedPlayer;
private Vec3 lockedPlayerPos;
private Vec3 previousCameraPos;
private Vec3 cameraPos;
private FakePlayerEntity cameraEntity;
private boolean wasNoGravity;
private boolean wasNoPhysics;
public Freecam() {
super("Freecam", Categories.MOVEMENT);
setDescription("Moves the camera without moving your player.");
addSetting(flySpeed);
}
public void setSpeed(float speed) {
this.flySpeed.setValue(speed);
}
public double getSpeed() {
return flySpeed.getValue();
}
@Override
public void onEnable() {
EventRegistry.PACKET_EVENT.register(packetListener);
ensureInitialized();
syncCameraEntity(0.0F);
invalidateChunks();
}
@Override
public void onDisable() {
EventRegistry.PACKET_EVENT.unregister(packetListener);
restorePlayerState();
restoreCamera();
invalidateChunks();
clearState();
}
@Override
public void onTick() {
if (mc.player == null || mc.level == null) {
return;
}
if (!ensureInitialized()) {
return;
}
lockPlayer();
previousCameraPos = cameraPos;
moveCamera();
syncCameraEntity(0.0F);
}
private void onPacket(PacketEvent event) {
if (event.getDirection() != PacketEvent.Direction.SEND) {
return;
}
if (event.getPacket() instanceof ServerboundMovePlayerPacket) {
event.cancel();
}
}
private boolean ensureInitialized() {
Player player = mc.player;
if (player == null || mc.level == null) {
return false;
}
boolean playerChanged = player != lockedPlayer;
boolean levelChanged = cameraPos == null;
if (playerChanged || levelChanged || cameraEntity == null) {
if (cameraEntity != null) {
cameraEntity.despawn();
}
lockedPlayer = player;
lockedPlayerPos = new Vec3(player.getX(), player.getY(), player.getZ());
cameraPos = new Vec3(
player.getX(),
player.getY() + player.getEyeHeight(),
player.getZ()
);
previousCameraPos = cameraPos;
cameraEntity = new FakePlayerEntity();
cameraEntity.setNoGravity(true);
((EntityAccessor) cameraEntity).setNoPhysics(true);
wasNoGravity = player.isNoGravity();
wasNoPhysics = ((EntityAccessor) player).isNoPhysics();
}
return true;
}
private void lockPlayer() {
if (lockedPlayer == null || lockedPlayerPos == null) {
return;
}
lockedPlayer.setPos(lockedPlayerPos.x, lockedPlayerPos.y, lockedPlayerPos.z);
lockedPlayer.setDeltaMovement(Vec3.ZERO);
lockedPlayer.setNoGravity(true);
((EntityAccessor) lockedPlayer).setNoPhysics(true);
syncInterpolation(lockedPlayer);
}
private void moveCamera() {
if (cameraPos == null || lockedPlayer == null) {
return;
}
double step = flySpeed.getValue() / 20.0D;
if (mc.options.keySprint.isDown()) {
step *= 2.0D;
}
Vec3 velocity = Vec3.ZERO;
Vec3 forward = Vec3.directionFromRotation(0.0F, lockedPlayer.getYRot());
Vec3 right = Vec3.directionFromRotation(0.0F, lockedPlayer.getYRot() + 90.0F);
if (mc.options.keyUp.isDown()) {
velocity = velocity.add(forward.scale(step));
} else if (mc.options.keyDown.isDown()) {
velocity = velocity.subtract(forward.scale(step));
}
if (mc.options.keyRight.isDown()) {
velocity = velocity.add(right.scale(step));
} else if (mc.options.keyLeft.isDown()) {
velocity = velocity.subtract(right.scale(step));
}
if (mc.options.keyJump.isDown()) {
velocity = velocity.add(0.0D, step, 0.0D);
} else if (mc.options.keyShift.isDown()) {
velocity = velocity.add(0.0D, -step, 0.0D);
}
if (velocity.x == 0.0D && velocity.y == 0.0D && velocity.z == 0.0D) {
return;
}
cameraPos = cameraPos.add(velocity);
}
public void applyCamera(Camera camera, float partialTick) {
syncCameraEntity(partialTick);
}
private void syncCameraEntity(float partialTick) {
if (cameraEntity == null || lockedPlayer == null || cameraPos == null) {
return;
}
float yaw = lockedPlayer.getViewYRot(partialTick);
float pitch = lockedPlayer.getViewXRot(partialTick);
double eyeHeight = cameraEntity.getEyeHeight();
Vec3 currentFeetPos = new Vec3(
cameraPos.x,
cameraPos.y - eyeHeight,
cameraPos.z
);
Vec3 previousFeetPos = previousCameraPos != null ? new Vec3(
previousCameraPos.x,
previousCameraPos.y - eyeHeight,
previousCameraPos.z
) : currentFeetPos;
cameraEntity.setPosRaw(currentFeetPos.x, currentFeetPos.y, currentFeetPos.z);
cameraEntity.setRot(yaw, pitch);
EntityAccessor accessor = (EntityAccessor) cameraEntity;
accessor.setXo(previousFeetPos.x);
accessor.setYo(previousFeetPos.y);
accessor.setZo(previousFeetPos.z);
accessor.setYRotO(yaw);
accessor.setXRotO(pitch);
mc.setCameraEntity(cameraEntity);
}
private void syncInterpolation(Entity entity) {
if (entity == null) {
return;
}
EntityAccessor accessor = (EntityAccessor) entity;
accessor.setXo(entity.getX());
accessor.setYo(entity.getY());
accessor.setZo(entity.getZ());
accessor.setYRotO(entity.getYRot());
accessor.setXRotO(entity.getXRot());
}
private void restorePlayerState() {
if (lockedPlayer == null) {
return;
}
if (lockedPlayerPos != null) {
lockedPlayer.setPos(lockedPlayerPos.x, lockedPlayerPos.y, lockedPlayerPos.z);
}
lockedPlayer.setDeltaMovement(Vec3.ZERO);
lockedPlayer.setNoGravity(wasNoGravity);
((EntityAccessor) lockedPlayer).setNoPhysics(wasNoPhysics);
}
private void restoreCamera() {
if (cameraEntity != null) {
cameraEntity.despawn();
cameraEntity = null;
}
if (mc.player != null) {
mc.setCameraEntity(mc.player);
}
}
private void invalidateChunks() {
if (mc.levelRenderer != null) {
mc.levelRenderer.allChanged();
}
}
private void clearState() {
lockedPlayer = null;
lockedPlayerPos = null;
previousCameraPos = null;
cameraPos = null;
cameraEntity = null;
wasNoGravity = false;
wasNoPhysics = false;
}
@Override
public Extension createModule() {
return this;
}
}
package io.github.ensamisten.client.mixin;
import io.github.ensamisten.client.module.Extension;
import io.github.ensamisten.client.module.ModuleManager;
import io.github.ensamisten.client.module.movement.Freecam;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.GameRenderer;
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;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Inject(method = "update", at = @At("HEAD"))
private void applyFreecamCamera(DeltaTracker deltaTracker, boolean advanceGameTime, CallbackInfo ci) {
ModuleManager.getModule(Freecam.class)
.filter(Extension::isEnabled)
.ifPresent(freecam -> freecam.applyCamera(
((GameRenderer) (Object) this).getMainCamera(),
deltaTracker.getGameTimeDeltaPartialTick(false)
));
}
}
package io.github.ensamisten.client.mixin;
import io.github.ensamisten.client.module.Extension;
import io.github.ensamisten.client.module.ModuleManager;
import io.github.ensamisten.client.module.movement.Freecam;
import net.minecraft.client.renderer.chunk.VisGraph;
import net.minecraft.client.renderer.chunk.VisibilitySet;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(VisGraph.class)
public class VisGraphMixin {
@Inject(method = "resolve", at = @At("RETURN"), cancellable = true)
private void allyship$forceFullVisibility(CallbackInfoReturnable<VisibilitySet> cir) {
ModuleManager.getModule(Freecam.class)
.filter(Extension::isEnabled)
.ifPresent(_ -> {
VisibilitySet visibilitySet = cir.getReturnValue();
if (visibilitySet != null) {
visibilitySet.setAll(true);
cir.setReturnValue(visibilitySet);
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment