Last active
June 7, 2026 12:29
-
-
Save Ensamisten/b3192020bdd9682dfcf3df6c4d4e1499 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) | |
| )); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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