diff --git a/build.gradle.kts b/build.gradle.kts index 0cc3ed3..167ecac 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 { diff --git a/src/main/java/codes/som/hibiscus/mixins/MixinClientPlayerEntity.java b/src/main/java/codes/som/hibiscus/mixins/MixinClientPlayerEntity.java index a0e874f..e4816d6 100644 --- a/src/main/java/codes/som/hibiscus/mixins/MixinClientPlayerEntity.java +++ b/src/main/java/codes/som/hibiscus/mixins/MixinClientPlayerEntity.java @@ -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); diff --git a/src/main/java/codes/som/hibiscus/mixins/MixinExtPlayerInteractEntityC2SPacket.java b/src/main/java/codes/som/hibiscus/mixins/MixinExtPlayerInteractEntityC2SPacket.java index 61aeb64..0caf9a2 100644 --- a/src/main/java/codes/som/hibiscus/mixins/MixinExtPlayerInteractEntityC2SPacket.java +++ b/src/main/java/codes/som/hibiscus/mixins/MixinExtPlayerInteractEntityC2SPacket.java @@ -8,4 +8,7 @@ import org.spongepowered.asm.mixin.gen.Accessor; public interface MixinExtPlayerInteractEntityC2SPacket { @Accessor("type") PlayerInteractEntityC2SPacket.InteractTypeHandler getTypeHandler(); + + @Accessor("entityId") + int getEntityId(); } diff --git a/src/main/java/codes/som/hibiscus/mixins/MixinGameRenderer.java b/src/main/java/codes/som/hibiscus/mixins/MixinGameRenderer.java index 404d2e8..a4dd0da 100644 --- a/src/main/java/codes/som/hibiscus/mixins/MixinGameRenderer.java +++ b/src/main/java/codes/som/hibiscus/mixins/MixinGameRenderer.java @@ -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(); + } } diff --git a/src/main/java/codes/som/hibiscus/mixins/MixinMinecraftClient.java b/src/main/java/codes/som/hibiscus/mixins/MixinMinecraftClient.java index b683282..e9fbc93 100644 --- a/src/main/java/codes/som/hibiscus/mixins/MixinMinecraftClient.java +++ b/src/main/java/codes/som/hibiscus/mixins/MixinMinecraftClient.java @@ -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 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); + } } diff --git a/src/main/java/codes/som/hibiscus/mixins/MixinWorldRenderer.java b/src/main/java/codes/som/hibiscus/mixins/MixinWorldRenderer.java new file mode 100644 index 0000000..49e88cc --- /dev/null +++ b/src/main/java/codes/som/hibiscus/mixins/MixinWorldRenderer.java @@ -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)); + } +} diff --git a/src/main/kotlin/codes/som/hibiscus/HibiscusMod.kt b/src/main/kotlin/codes/som/hibiscus/HibiscusMod.kt index f33e0b1..b56e4fc 100644 --- a/src/main/kotlin/codes/som/hibiscus/HibiscusMod.kt +++ b/src/main/kotlin/codes/som/hibiscus/HibiscusMod.kt @@ -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().enabled = true + features.getFeature().enabled = true + features.getFeature().enabled = true + } } diff --git a/src/main/kotlin/codes/som/hibiscus/api/feature/Feature.kt b/src/main/kotlin/codes/som/hibiscus/api/feature/Feature.kt index 8612bfd..01d556a 100644 --- a/src/main/kotlin/codes/som/hibiscus/api/feature/Feature.kt +++ b/src/main/kotlin/codes/som/hibiscus/api/feature/Feature.kt @@ -31,6 +31,8 @@ abstract class Feature(val name: String, val category: FeatureCategory) { } } + var hiddenInOverlay = false + open fun onEnable() {} open fun onDisable() {} diff --git a/src/main/kotlin/codes/som/hibiscus/api/feature/FeatureCategory.kt b/src/main/kotlin/codes/som/hibiscus/api/feature/FeatureCategory.kt index 5b6e855..979c88e 100644 --- a/src/main/kotlin/codes/som/hibiscus/api/feature/FeatureCategory.kt +++ b/src/main/kotlin/codes/som/hibiscus/api/feature/FeatureCategory.kt @@ -6,4 +6,5 @@ enum class FeatureCategory(val humanName: String) { PLAYER("Player"), MOVEMENT("Movement"), OVERLAY("Overlay"), + VISUAL("Visual"), } diff --git a/src/main/kotlin/codes/som/hibiscus/events/PlayerEvents.kt b/src/main/kotlin/codes/som/hibiscus/events/PlayerEvents.kt index 62274c7..59bc30b 100644 --- a/src/main/kotlin/codes/som/hibiscus/events/PlayerEvents.kt +++ b/src/main/kotlin/codes/som/hibiscus/events/PlayerEvents.kt @@ -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 diff --git a/src/main/kotlin/codes/som/hibiscus/events/RenderEvents.kt b/src/main/kotlin/codes/som/hibiscus/events/RenderEvents.kt index c15d8ae..f751644 100644 --- a/src/main/kotlin/codes/som/hibiscus/events/RenderEvents.kt +++ b/src/main/kotlin/codes/som/hibiscus/events/RenderEvents.kt @@ -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 diff --git a/src/main/kotlin/codes/som/hibiscus/features/FeaturesRegistry.kt b/src/main/kotlin/codes/som/hibiscus/features/FeaturesRegistry.kt index c597d81..1502448 100644 --- a/src/main/kotlin/codes/som/hibiscus/features/FeaturesRegistry.kt +++ b/src/main/kotlin/codes/som/hibiscus/features/FeaturesRegistry.kt @@ -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 { diff --git a/src/main/kotlin/codes/som/hibiscus/features/overlay/Overlay.kt b/src/main/kotlin/codes/som/hibiscus/features/overlay/Overlay.kt index 44003a6..6dd8a38 100644 --- a/src/main/kotlin/codes/som/hibiscus/features/overlay/Overlay.kt +++ b/src/main/kotlin/codes/som/hibiscus/features/overlay/Overlay.kt @@ -7,13 +7,12 @@ 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) { init { val enabledFeatures by values.bool("Enabled Features", true) - + on { _: PostRenderAllEvent -> if (mc.world == null || mc.options.debugEnabled) return@on @@ -24,10 +23,10 @@ class Overlay : Feature("Overlay", FeatureCategory.OVERLAY) { ImGui.pushStyleColor(ImGuiCol.WindowBg, 0, 0, 0, 0) ImGui.begin( - "Overlay##Overlay", + "Overlay##Overlay", ImGuiWindowFlags.NoMove + ImGuiWindowFlags.NoInputs + ImGuiWindowFlags.NoDecoration ) - + if (enabledFeatures) drawEnabledFeatures() @@ -37,35 +36,35 @@ class Overlay : Feature("Overlay", FeatureCategory.OVERLAY) { enabled = true } - + 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 val padY = 8f - + val pos = ImGui.getCursorScreenPos() ImGui.setCursorScreenPos(pos.x + padX, pos.y + padY) - + ImGui.beginGroup() - + ImGui.pushStyleColor(ImGuiCol.Text, 244, 161, 255, 255) - for (feature in enabledFeatures) + for (feature in displayedFeatures) ImGui.text(feature.name) ImGui.popStyleColor() - + ImGui.endGroup() val min = ImGui.getItemRectMin() val max = ImGui.getItemRectMax() ImGui.getBackgroundDrawList().addRectFilled( min.x - padX, min.y - padY, max.x + padX, - max.y + padY, + max.y + padY, 0xAA0A0A0AL.toInt(), 0F ) diff --git a/src/main/kotlin/codes/som/hibiscus/features/visual/Freecam.kt b/src/main/kotlin/codes/som/hibiscus/features/visual/Freecam.kt new file mode 100644 index 0000000..d6b941b --- /dev/null +++ b/src/main/kotlin/codes/som/hibiscus/features/visual/Freecam.kt @@ -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(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 + } +} diff --git a/src/main/kotlin/codes/som/hibiscus/util/graphics/MinecraftRenderPipelineProgress.kt b/src/main/kotlin/codes/som/hibiscus/util/graphics/MinecraftRenderPipelineProgress.kt new file mode 100644 index 0000000..487d563 --- /dev/null +++ b/src/main/kotlin/codes/som/hibiscus/util/graphics/MinecraftRenderPipelineProgress.kt @@ -0,0 +1,7 @@ +package codes.som.hibiscus.util.graphics + +object MinecraftRenderPipelineProgress { + var isRenderingEntities: Boolean = false + var isDrawingUI: Boolean = false + var isProcessingInput: Boolean = false +} diff --git a/src/main/resources/hibiscus.mixins.json b/src/main/resources/hibiscus.mixins.json index 0b54c61..afe4d1a 100644 --- a/src/main/resources/hibiscus.mixins.json +++ b/src/main/resources/hibiscus.mixins.json @@ -16,6 +16,7 @@ "MixinExtUpdatePlayerAbilitiesC2SPacket", "MixinGameRenderer", "MixinKeyboard", - "MixinMinecraftClient" + "MixinMinecraftClient", + "MixinWorldRenderer" ] }