Add the 'Freecam' feature

main
Charlotte Som 2022-02-14 03:08:39 +00:00
parent f3f3b21848
commit f64139b600
16 changed files with 442 additions and 20 deletions

View File

@ -54,8 +54,10 @@ dependencies {
runtimeOnly("org.joml:joml:1.10.2")
runtimeOnly("org.anarres:jcpp:1.4.14")
modRuntimeOnly(files("vendor/mods/iris-mc1.18.1-1.2.0-pre.jar"))
modRuntimeOnly(files("vendor/mods/sodium-fabric-mc1.18.1-0.4.0-alpha6+build.14.jar"))
modRuntimeOnly(files("vendor/mods/lazydfu-0.1.2.jar"))
}
loom {

View File

@ -2,6 +2,7 @@ package codes.som.hibiscus.mixins;
import codes.som.hibiscus.HibiscusMod;
import codes.som.hibiscus.events.MovePlayerEvent;
import codes.som.hibiscus.events.PlayerPreTickEvent;
import codes.som.hibiscus.events.PlayerTickEvent;
import codes.som.hibiscus.events.SendChatEvent;
import net.minecraft.client.network.ClientPlayerEntity;
@ -16,6 +17,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayerEntity.class)
public abstract class MixinClientPlayerEntity {
@Inject(method = "tick", at = @At("HEAD"))
private void onPreTick(CallbackInfo ci) {
HibiscusMod.bus().fire(PlayerPreTickEvent.INSTANCE);
}
@Inject(method = "tick", at = @At("RETURN"))
private void onPostTick(CallbackInfo ci) {
HibiscusMod.bus().fire(PlayerTickEvent.INSTANCE);

View File

@ -8,4 +8,7 @@ import org.spongepowered.asm.mixin.gen.Accessor;
public interface MixinExtPlayerInteractEntityC2SPacket {
@Accessor("type")
PlayerInteractEntityC2SPacket.InteractTypeHandler getTypeHandler();
@Accessor("entityId")
int getEntityId();
}

View File

@ -1,10 +1,13 @@
package codes.som.hibiscus.mixins;
import codes.som.hibiscus.HibiscusMod;
import codes.som.hibiscus.events.RenderWorldEvent;
import codes.som.hibiscus.events.Render3DEvent;
import codes.som.hibiscus.events.RenderHandEvent;
import codes.som.hibiscus.util.graphics.MinecraftRenderPipelineProgress;
import codes.som.hibiscus.util.graphics.WorldToScreenProjection;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
@ -14,6 +17,7 @@ import org.spongepowered.asm.mixin.Mixin;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
@ -40,7 +44,32 @@ public abstract class MixinGameRenderer {
matrices.push();
RenderSystem.applyModelViewMatrix();
HibiscusMod.bus().fire(new RenderWorldEvent(tickDelta, this.camera, matrices));
HibiscusMod.bus().fire(new Render3DEvent(tickDelta, this.camera, matrices));
matrices.pop();
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;render(Lnet/minecraft/client/util/math/MatrixStack;F)V"))
private void callRenderOverlay(InGameHud inGameHud, MatrixStack matrices, float tickDelta) {
MinecraftRenderPipelineProgress.INSTANCE.setDrawingUI(true);
inGameHud.render(matrices, tickDelta);
MinecraftRenderPipelineProgress.INSTANCE.setDrawingUI(false);
}
@Inject(method = "updateTargetedEntity", at = @At("HEAD"))
public void onPreUpdateTargetedEntity(float tickDelta, CallbackInfo ci) {
MinecraftRenderPipelineProgress.INSTANCE.setProcessingInput(true);
}
@Inject(method = "updateTargetedEntity", at = @At("RETURN"))
public void onPostUpdateTargetedEntity(float tickDelta, CallbackInfo ci) {
MinecraftRenderPipelineProgress.INSTANCE.setProcessingInput(false);
}
@Inject(method = "renderHand", at = @At("HEAD"), cancellable = true)
private void onRenderHand(MatrixStack matrices, Camera camera, float tickDelta, CallbackInfo ci) {
var event = new RenderHandEvent(tickDelta);
HibiscusMod.bus().fire(event);
if (event.isCancelled())
ci.cancel();
}
}

View File

@ -1,17 +1,21 @@
package codes.som.hibiscus.mixins;
import codes.som.hibiscus.HibiscusMod;
import codes.som.hibiscus.events.GetRenderViewEntityEvent;
import codes.som.hibiscus.events.PostRenderAllEvent;
import codes.som.hibiscus.gui.ImGuiRenderer;
import codes.som.hibiscus.util.graphics.MinecraftRenderPipelineProgress;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.RunArgs;
import net.minecraft.client.render.RenderTickCounter;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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(MinecraftClient.class)
public abstract class MixinMinecraftClient {
@ -42,4 +46,25 @@ public abstract class MixinMinecraftClient {
HibiscusMod.bus().fire(new PostRenderAllEvent(delta));
ImGuiRenderer.INSTANCE.finishFrame(delta);
}
@Inject(method = "getCameraEntity", at = @At("RETURN"), cancellable = true)
private void onGetCameraEntity(CallbackInfoReturnable<Entity> cir) {
Entity view = cir.getReturnValue();
if (view != null) {
var event = new GetRenderViewEntityEvent(view);
HibiscusMod.bus().fire(event);
cir.setReturnValue(event.getViewEntity());
}
}
@Inject(method = "handleInputEvents", at = @At("HEAD"))
private void onPreHandleInputEvents(CallbackInfo ci) {
MinecraftRenderPipelineProgress.INSTANCE.setProcessingInput(true);
}
@Inject(method = "handleInputEvents", at = @At("RETURN"))
private void onPostHandleInputEvents(CallbackInfo ci) {
MinecraftRenderPipelineProgress.INSTANCE.setProcessingInput(false);
}
}

View File

@ -0,0 +1,49 @@
package codes.som.hibiscus.mixins;
import codes.som.hibiscus.HibiscusMod;
import codes.som.hibiscus.events.*;
import codes.som.hibiscus.util.graphics.MinecraftRenderPipelineProgress;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Matrix4f;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(WorldRenderer.class)
public abstract class MixinWorldRenderer {
@Inject(method = "render", at = @At("HEAD"))
private void onPreRenderWorld(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) {
HibiscusMod.bus().fire(new PreRenderWorldEvent(tickDelta, camera, matrices));
}
@Inject(method = "render", at = @At("HEAD"))
private void onPostRenderWorld(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) {
HibiscusMod.bus().fire(new PostRenderWorldEvent(tickDelta, camera, matrices));
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isSpectator()Z"))
public boolean cancelCulling(ClientPlayerEntity instance) {
var event = new WorldCullingEvent();
HibiscusMod.bus().fire(event);
return instance.isSpectator() || event.isCancelled();
}
@Inject(method = "render", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V", args = "ldc=entities"))
private void startRenderEntities(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) {
MinecraftRenderPipelineProgress.INSTANCE.setRenderingEntities(true);
HibiscusMod.bus().fire(new PreRenderEntitiesEvent(tickDelta));
}
@Inject(method = "render", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V", args = "ldc=blockentities"))
private void startRenderBlockEntities(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) {
MinecraftRenderPipelineProgress.INSTANCE.setRenderingEntities(false);
HibiscusMod.bus().fire(new PostRenderEntitiesEvent(tickDelta));
}
}

View File

@ -6,13 +6,16 @@ import codes.som.hibiscus.api.keybinds.KeybindRegistry
import codes.som.hibiscus.commands.CommandRegistry
import codes.som.hibiscus.events.KeyEvent
import codes.som.hibiscus.features.FeaturesRegistry
import codes.som.hibiscus.features.combat.Criticals
import codes.som.hibiscus.features.exploits.AntiGhost
import codes.som.hibiscus.features.player.NoFallDamage
import codes.som.hibiscus.gui.ImGuiScreen
import codes.som.hibiscus.util.command.ChatCommandListener
import codes.som.hibiscus.util.input.KeybindDispatcher
import codes.som.hibiscus.util.netmoving.NetworkMovingDispatcher
import net.fabricmc.api.ModInitializer
import org.lwjgl.glfw.GLFW
import org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SHIFT
import org.lwjgl.glfw.GLFW.*
@Suppress("UNUSED")
object HibiscusMod : ModInitializer {
@ -45,6 +48,7 @@ object HibiscusMod : ModInitializer {
bus.register(ChatCommandListener())
bus.register(KeybindDispatcher())
defaultConfig()
// TODO: Load files
}
@ -55,4 +59,15 @@ object HibiscusMod : ModInitializer {
system.reset()
}
}
fun defaultConfig() {
keybinds.register(GLFW_KEY_R, "fly")
keybinds.register(GLFW_KEY_O, "nofall")
keybinds.register(GLFW_KEY_G, "speed")
keybinds.register(GLFW_KEY_B, "freecam")
features.getFeature<AntiGhost>().enabled = true
features.getFeature<NoFallDamage>().enabled = true
features.getFeature<Criticals>().enabled = true
}
}

View File

@ -31,6 +31,8 @@ abstract class Feature(val name: String, val category: FeatureCategory) {
}
}
var hiddenInOverlay = false
open fun onEnable() {}
open fun onDisable() {}

View File

@ -6,4 +6,5 @@ enum class FeatureCategory(val humanName: String) {
PLAYER("Player"),
MOVEMENT("Movement"),
OVERLAY("Overlay"),
VISUAL("Visual"),
}

View File

@ -4,6 +4,7 @@ import codes.som.hibiscus.api.event.Cancellable
import codes.som.hibiscus.api.event.Event
import net.minecraft.entity.MovementType
object PlayerPreTickEvent : Event
object PlayerTickEvent : Event
class SendChatEvent(val message: String) : Cancellable(), Event
data class MovePlayerEvent(var x: Double, var y: Double, var z: Double, val movementType: MovementType) : Event

View File

@ -1,8 +1,21 @@
package codes.som.hibiscus.events
import codes.som.hibiscus.api.event.Cancellable
import codes.som.hibiscus.api.event.Event
import net.minecraft.client.render.Camera
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.Entity
class PostRenderAllEvent(val delta: Float) : Event
class RenderWorldEvent(val delta: Float, val camera: Camera, val matrices: MatrixStack) : Event
class Render3DEvent(val delta: Float, val camera: Camera, val matrices: MatrixStack) : Event
class WorldCullingEvent : Cancellable(), Event
class GetRenderViewEntityEvent(var viewEntity: Entity) : Event
class PreRenderWorldEvent(val delta: Float, val camera: Camera, val matrices: MatrixStack) : Event
class PreRenderEntitiesEvent(val delta: Float) : Event
class PostRenderEntitiesEvent(val delta: Float) : Event
class PostRenderWorldEvent(val delta: Float, val camera: Camera, val matrices: MatrixStack) : Event
class RenderHandEvent(val delta: Float) : Cancellable(), Event

View File

@ -8,6 +8,7 @@ import codes.som.hibiscus.features.movement.Flight
import codes.som.hibiscus.features.movement.Speed
import codes.som.hibiscus.features.overlay.Overlay
import codes.som.hibiscus.features.player.NoFallDamage
import codes.som.hibiscus.features.visual.Freecam
import codes.som.hibiscus.util.Resettable
fun allFeatureClasses(): Array<() -> Feature> = arrayOf(
@ -18,6 +19,7 @@ fun allFeatureClasses(): Array<() -> Feature> = arrayOf(
::Criticals,
::Ghost,
::AntiGhost,
::Freecam,
)
class FeaturesRegistry : Resettable {

View File

@ -7,7 +7,6 @@ import codes.som.hibiscus.events.PostRenderAllEvent
import codes.som.hibiscus.mc
import imgui.ImGui
import imgui.flag.ImGuiCol
import imgui.flag.ImGuiStyleVar
import imgui.flag.ImGuiWindowFlags
class Overlay : Feature("Overlay", FeatureCategory.OVERLAY) {
@ -39,12 +38,12 @@ class Overlay : Feature("Overlay", FeatureCategory.OVERLAY) {
}
private fun drawEnabledFeatures() {
val enabledFeatures =
val displayedFeatures =
HibiscusMod.features.getAllFeatures()
.filter { it.category != FeatureCategory.OVERLAY }
.filter { !it.hiddenInOverlay }
.filter { it.enabled }.toList()
if (enabledFeatures.isEmpty())
if (displayedFeatures.isEmpty())
return
val padX = 8f
@ -56,7 +55,7 @@ class Overlay : Feature("Overlay", FeatureCategory.OVERLAY) {
ImGui.beginGroup()
ImGui.pushStyleColor(ImGuiCol.Text, 244, 161, 255, 255)
for (feature in enabledFeatures)
for (feature in displayedFeatures)
ImGui.text(feature.name)
ImGui.popStyleColor()

View File

@ -0,0 +1,267 @@
package codes.som.hibiscus.features.visual
import codes.som.hibiscus.HibiscusMod
import codes.som.hibiscus.api.feature.Feature
import codes.som.hibiscus.api.feature.FeatureCategory
import codes.som.hibiscus.events.*
import codes.som.hibiscus.mc
import codes.som.hibiscus.mixins.MixinExtPlayerInteractEntityC2SPacket
import codes.som.hibiscus.player
import codes.som.hibiscus.util.ext.requireExtension
import codes.som.hibiscus.util.graphics.MinecraftRenderPipelineProgress
import codes.som.hibiscus.world
import net.minecraft.client.input.Input
import net.minecraft.client.network.ClientPlayerEntity
import net.minecraft.client.option.Perspective
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityType
import net.minecraft.entity.MovementType
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket
import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import org.lwjgl.glfw.GLFW.GLFW_KEY_TAB
import org.lwjgl.glfw.GLFW.GLFW_PRESS
class Freecam : Feature("Freecam", FeatureCategory.VISUAL) {
private var camEntity: Entity? = null
private var playerInput: Input? = null
private var isControllingPlayer: Boolean = false
private var origYaw = 0F
private var origPitch = 0F
private val toggleKey by values.key("Toggle Key", GLFW_KEY_TAB)
init {
var isPlayerTicking = false
on { _: PlayerPreTickEvent -> isPlayerTicking = true }
on { _: PlayerTickEvent -> isPlayerTicking = false }
on { event: WorldCullingEvent -> event.cancel() }
on { event: GetRenderViewEntityEvent ->
val view = camEntity ?: return@on
if (isPlayerTicking)
return@on
if (MinecraftRenderPipelineProgress.isRenderingEntities || MinecraftRenderPipelineProgress.isDrawingUI)
return@on
if (isControllingPlayer && MinecraftRenderPipelineProgress.isProcessingInput)
return@on
event.viewEntity = view
}
on { _: PlayerPreTickEvent ->
val view = camEntity ?: return@on
if (isControllingPlayer) {
origYaw = player.yaw
origPitch = player.pitch
}
player.yaw = origYaw
player.pitch = origPitch
with(view) {
prevHorizontalSpeed = horizontalSpeed
prevX = x
prevY = y
prevZ = z
prevPitch = pitch
prevYaw = yaw
}
if (!isControllingPlayer) {
val yaw = MathHelper.RADIANS_PER_DEGREE * view.yaw
val lookVec = Vec3d(-MathHelper.sin(yaw).toDouble(), 0.0, MathHelper.cos(yaw).toDouble())
var movement = Vec3d.ZERO
if (mc.options.forwardKey.isPressed)
movement = movement.add(0.0, 0.0, 1.0)
if (mc.options.backKey.isPressed)
movement = movement.add(0.0, 0.0, -1.0)
if (mc.options.rightKey.isPressed)
movement = movement.add(1.0, 0.0, 0.0)
if (mc.options.leftKey.isPressed)
movement = movement.add(-1.0, 0.0, 0.0)
if (mc.options.jumpKey.isPressed)
movement = movement.add(0.0, 1.0, 0.0)
if (mc.options.sneakKey.isPressed)
movement = movement.add(0.0, -1.0, 0.0)
// one day i will learn the matrix math for this
movement = lookVec
.multiply(movement.z)
.add(lookVec.rotateY(-MathHelper.HALF_PI).multiply(movement.x))
.add(0.0, movement.y, 0.0)
view.move(MovementType.SELF, movement) // Update entity positional state properly by calling move()
}
}
on { event: KeyEvent ->
if (event.action == GLFW_PRESS && event.key == toggleKey) {
isControllingPlayer = !isControllingPlayer
updatePlayerControl()
}
}
var yawPreRender = 0F
var pitchPreRender = 0F
var prevPitchPreRender = 0F
on { _: PreRenderWorldEvent ->
yawPreRender = player.yaw
pitchPreRender = player.pitch
prevPitchPreRender = player.prevPitch
camEntity?.let { view ->
player.yaw = view.yaw
player.pitch = view.pitch
player.prevPitch = view.prevPitch
}
}
var lastMouseX = 0.0
var lastMouseY = 0.0
var mouseDeltaX = 0.0
var mouseDeltaY = 0.0
HibiscusMod.bus.register { _: PostRenderWorldEvent ->
mouseDeltaX = mc.mouse.x - lastMouseX
mouseDeltaY = mc.mouse.y - lastMouseY
lastMouseX = mc.mouse.x
lastMouseY = mc.mouse.y
}
// TODO: On post render world, update the camEntity's view angles based on mouse delta
on { _: PostRenderWorldEvent ->
val view = camEntity ?: return@on
if (isControllingPlayer)
return@on
if (mc.isWindowFocused && mc.currentScreen == null) {
val mouseSensAdj = mc.options.mouseSensitivity * 0.6 + 0.2
val mouseSensAngleCoeff = mouseSensAdj * mouseSensAdj * mouseSensAdj * 8.0
val invertYCoeff = if (mc.options.invertYMouse) -1 else 1
view.changeLookDirection(
mouseDeltaX * mouseSensAngleCoeff,
mouseDeltaY * mouseSensAngleCoeff * invertYCoeff
)
}
}
// TODO: Push / pop origYaw so that the player matches their server-side look angles
var perspectivePreRender: Perspective? = null
on { event: PreRenderEntitiesEvent ->
player.yaw = yawPreRender
player.pitch = pitchPreRender
player.prevPitch = prevPitchPreRender
if (!isControllingPlayer) {
player.yaw = origYaw
player.pitch = origPitch
}
perspectivePreRender = mc.options.perspective
mc.options.perspective = Perspective.THIRD_PERSON_BACK
mc.gameRenderer.camera.update(mc.world, mc.player, true, false, event.delta)
}
on { event: PostRenderEntitiesEvent ->
mc.options.perspective = perspectivePreRender!!
player.yaw = yawPreRender
player.pitch = pitchPreRender
player.prevPitch = prevPitchPreRender
mc.gameRenderer.camera.update(
mc.world,
mc.getCameraEntity() ?: mc.player,
!mc.options.perspective.isFirstPerson,
mc.options.perspective.isFrontView,
event.delta
)
}
on { event: SendPacketEvent ->
val (packet) = event
if (packet is PlayerInteractEntityC2SPacket) {
requireExtension<MixinExtPlayerInteractEntityC2SPacket>(packet)
if (packet.typeHandler.type == PlayerInteractEntityC2SPacket.InteractType.ATTACK && packet.entityId == player.id) {
event.cancel()
}
}
}
on { event: RenderHandEvent ->
if (!isControllingPlayer)
event.cancel()
}
}
private fun updatePlayerControl() {
if (isControllingPlayer) {
if (playerInput != null)
player.input = playerInput
} else {
playerInput = player.input
player.input = Input()
player.input.sneaking = playerInput!!.sneaking
}
}
private fun setupCamera() {
isControllingPlayer = false
camEntity = FreecamControllerEntity(player, world)
updatePlayerControl()
origYaw = player.yaw
origPitch = player.pitch
}
override fun onEnable() {
if (mc.world == null)
return
setupCamera()
}
override fun onDisable() {
if (mc.world == null)
return
isControllingPlayer = true
updatePlayerControl()
camEntity = null
playerInput = null
}
class FreecamControllerEntity(localPlayer: ClientPlayerEntity, world: World) : Entity(EntityType.PLAYER, world) {
init {
updatePositionAndAngles(
localPlayer.x, localPlayer.y, localPlayer.z,
localPlayer.yaw, localPlayer.pitch
)
setRotation(localPlayer.yaw, localPlayer.pitch)
noClip = true
setNoGravity(true)
}
override fun initDataTracker() {
}
override fun readCustomDataFromNbt(nbt: NbtCompound?) {
}
override fun writeCustomDataToNbt(nbt: NbtCompound?) {
}
override fun createSpawnPacket() = null
}
}

View File

@ -0,0 +1,7 @@
package codes.som.hibiscus.util.graphics
object MinecraftRenderPipelineProgress {
var isRenderingEntities: Boolean = false
var isDrawingUI: Boolean = false
var isProcessingInput: Boolean = false
}

View File

@ -16,6 +16,7 @@
"MixinExtUpdatePlayerAbilitiesC2SPacket",
"MixinGameRenderer",
"MixinKeyboard",
"MixinMinecraftClient"
"MixinMinecraftClient",
"MixinWorldRenderer"
]
}