From 5ae53024e85b2f6cb975e43b6ac0f102e12103c7 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 17 Dec 2025 01:50:13 +0100 Subject: [PATCH 01/20] Initial draft for 1.21.11 --- build.gradle.kts | 15 ++--- gradle.properties | 34 +++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- .../mixin/entity/ClientPlayerEntityMixin.java | 5 -- .../mixin/render/BackgroundRendererMixin.java | 56 ++++------------- .../mixin/render/DarknessEffectFogMixin.java | 41 ++++++++++++ .../mixin/render/DebugRendererMixin.java | 8 +-- .../mixin/render/GameRendererMixin.java | 12 ++-- src/main/kotlin/com/lambda/Lambda.kt | 3 +- .../brigadier/argument/PlayerArguments.kt | 3 +- .../com/lambda/command/CommandManager.kt | 2 +- .../kotlin/com/lambda/config/ConfigEditor.kt | 16 ++--- .../com/lambda/config/serializer/TextCodec.kt | 47 ++++++++++++++ .../kotlin/com/lambda/graphics/RenderMain.kt | 2 +- .../renderer/gui/AbstractGUIRenderer.kt | 3 +- .../com/lambda/graphics/shader/Shader.kt | 3 +- src/main/kotlin/com/lambda/gui/DearImGui.kt | 2 +- src/main/kotlin/com/lambda/gui/MenuBar.kt | 32 +++++----- .../lambda/gui/components/ClickGuiLayout.kt | 2 +- .../com/lambda/gui/components/QuickSearch.kt | 2 +- .../lambda/interaction/PlayerPacketHandler.kt | 16 ----- .../construction/simulation/Simulation.kt | 2 +- .../managers/breaking/BreakManager.kt | 6 +- .../rotating/visibilty/PlaceDirection.kt | 5 +- .../module/modules/combat/FakePlayer.kt | 6 +- .../lambda/module/modules/combat/KillAura.kt | 2 +- .../modules/debug/DebugRendererModule.kt | 63 ++++++++++--------- .../module/modules/render/ContainerPreview.kt | 21 ++++--- .../module/modules/render/MapPreview.kt | 18 +++--- .../lambda/module/modules/render/NoRender.kt | 10 --- .../kotlin/com/lambda/network/LambdaAPI.kt | 10 +-- .../kotlin/com/lambda/threading/Threading.kt | 2 +- src/main/kotlin/com/lambda/util/BlockUtils.kt | 2 - .../util/DynamicReflectionSerializer.kt | 4 +- .../com/lambda/util/player/PlayerUtils.kt | 2 +- .../kotlin/com/lambda/util/text/StyleDsl.kt | 6 +- .../kotlin/com/lambda/util/text/TextDsl.kt | 6 +- src/main/resources/lambda.accesswidener | 21 ++++--- src/main/resources/lambda.mixins.common.json | 1 + 39 files changed, 265 insertions(+), 228 deletions(-) create mode 100644 src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java create mode 100644 src/main/kotlin/com/lambda/config/serializer/TextCodec.kt diff --git a/build.gradle.kts b/build.gradle.kts index f2fd2347f..c50958bc1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,8 +30,6 @@ val discordIPCVersion: String by project val classGraphVersion: String by project val kotlinVersion: String by project val ktorVersion: String by project -val mockitoKotlin: String by project -val mockitoInline: String by project val mockkVersion: String by project val spairVersion: String by project val lwjglVersion: String by project @@ -46,10 +44,10 @@ val replacements = file("gradle.properties").inputStream().use { stream -> }.map { (k, v) -> k.toString() to v.toString() }.toMap() plugins { - kotlin("jvm") version "2.2.0" - id("org.jetbrains.dokka") version "2.0.0" - id("fabric-loom") version "1.10-SNAPSHOT" - id("com.gradleup.shadow") version "9.0.0-rc1" + kotlin("jvm") version "2.3.0" + id("org.jetbrains.dokka") version "2.1.0" + id("fabric-loom") version "1.14-SNAPSHOT" + id("com.gradleup.shadow") version "9.3.0" id("maven-publish") } @@ -175,7 +173,8 @@ dependencies { includeLib("io.ktor:ktor-serialization-gson:$ktorVersion") // Add mods - modImplementation("com.github.rfresh2:baritone-fabric:$minecraftVersion") + /*modImplementation("com.github.rfresh2:baritone-fabric:$minecraftVersion") */ + modImplementation("com.github.rfresh2:baritone-fabric:1.21.10-SNAPSHOT") // ToDo: Move to 1.21.11 modCompileOnly("maven.modrinth:sodium:$sodiumVersion") modCompileOnly("maven.modrinth:malilib:$maLiLibVersion") modCompileOnly("maven.modrinth:litematica:$litematicaVersion") @@ -185,8 +184,6 @@ dependencies { // Test implementations testImplementation(kotlin("test")) - testImplementation("org.mockito.kotlin:mockito-kotlin:$mockitoKotlin") - testImplementation("org.mockito:mockito-inline:$mockitoInline") testImplementation("io.mockk:mockk:${mockkVersion}") // Finish the configuration diff --git a/gradle.properties b/gradle.properties index 8e6c7f4d6..cbcea68f1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,38 +18,36 @@ # Lambda https://github.com/lambda-client/lambda modId=lambda modName=Lambda -modVersion=0.0.1 +modVersion=0.0.2 modDescription=Minecraft utility mod for automation modIcon=assets/lambda/lambda.png mavenGroup=com.lambda modAuthors=Constructor (Avanatiker), Blade, beanbag44, Emy # General -minecraftVersion=1.21.5 -minecraftVersionMin=1.21.5 -minecraftVersionMax=1.21.6 +minecraftVersion=1.21.11 +minecraftVersionMin=1.21.11 +minecraftVersionMax=1.21.11 lwjglVersion=3.3.3 -mixinExtrasVersion=0.5.0-rc.2 -kotlinVersion=2.2.0 +mixinExtrasVersion=0.5.0 +kotlinVersion=2.3.0 pngEncoderVersion=0.16.0 javaVersion=21 baritoneVersion=1.14.0 discordIPCVersion=6f6b6cce17 -classGraphVersion=4.8.179 -ktorVersion=3.1.2 -mockitoKotlin=5.4.0 -mockitoInline=5.2.0 -mockkVersion=1.13.17 +classGraphVersion=4.8.184 +ktorVersion=3.3.3 +mockkVersion=1.14.7 spairVersion=1.90.0 # Fabric https://fabricmc.net/develop/ -fabricLoaderVersion=0.16.13 -yarnMappings=build.1 -fabricApiVersion=0.124.0 -kotlinFabricVersion=1.13.4+kotlin -sodiumVersion=mc1.21.5-0.6.13-fabric -litematicaVersion=0.22.2 -maLiLibVersion=0.24.2 +fabricLoaderVersion=0.18.3 +yarnMappings=build.3 +fabricApiVersion=0.139.5 +kotlinFabricVersion=1.13.8+kotlin +sodiumVersion=mc1.21.11-0.8.0-fabric +litematicaVersion=0.25.1 +maLiLibVersion=0.27.1 # Kotlin https://kotlinlang.org/ kotlin.code.style=official diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c42ddc0c7..3d7f8165b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -17,7 +17,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java index 3b5a81c5d..bbb73599b 100644 --- a/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -85,11 +85,6 @@ void sendLambdaMovement(CallbackInfo ci) { autoJumpEnabled = Lambda.getMc().options.getAutoJump().getValue(); } - @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;sendSneakingPacket()V")) - void sendSneakingPacket(ClientPlayerEntity entity, Operation original) { - PlayerPacketHandler.sendSneakPackets(); - } - @ModifyExpressionValue(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isSprinting()Z")) boolean isSprinting(boolean original) { return EventFlow.post(new MovementEvent.Sprint(original)).getSprint(); diff --git a/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java b/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java index f0f02d1d2..b396db1b5 100644 --- a/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java @@ -18,60 +18,28 @@ package com.lambda.mixin.render; import com.lambda.module.modules.render.NoRender; -import com.lambda.module.modules.render.WorldColors; -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import net.minecraft.client.render.BackgroundRenderer; +import net.minecraft.client.render.fog.BlindnessEffectFogModifier; +import net.minecraft.client.render.fog.DarknessEffectFogModifier; +import net.minecraft.block.enums.CameraSubmersionType; import net.minecraft.entity.Entity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.biome.Biome; -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.CallbackInfoReturnable; -import java.util.List; -import java.util.stream.Stream; - /** - *
{@code
- * Vec3d vec3d2 = camera.getPos().subtract(2.0, 2.0, 2.0).multiply(0.25);
- * Vec3d vec3d3 = CubicSampler.sampleColor(
- *         vec3d2, (x, y, z) -> world.getDimensionEffects().adjustFogColor(Vec3d.unpackRgb(biomeAccess.getBiomeForNoiseGen(x, y, z).value().getFogColor()), v)
- * );
- * red = (float)vec3d3.getX();
- * green = (float)vec3d3.getY();
- * blue = (float)vec3d3.getZ();
- * }
+ * Mixins to disable blindness and darkness fog effects when NoRender is enabled. + * + * Note: The fog system was completely rewritten in 1.21.11. + * BackgroundRenderer was replaced with FogRenderer and fog modifiers are now separate classes. */ -@Mixin(BackgroundRenderer.class) +@Mixin(BlindnessEffectFogModifier.class) public class BackgroundRendererMixin { - @Shadow @Final private static List FOG_MODIFIERS; - - /** - * Modifies the fog color returned from CubicSampler.sampleColor - */ - @ModifyExpressionValue(method = "getFogColor", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/CubicSampler;sampleColor(Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/util/CubicSampler$RgbFetcher;)Lnet/minecraft/util/math/Vec3d;")) - private static Vec3d modifyFogColor(Vec3d original) { - return WorldColors.fogOfWarColor(original); - } - - @ModifyExpressionValue(method = "getFogColor", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/biome/Biome;getWaterFogColor()I")) - private static int modifyWaterFogColor(int original) { - return WorldColors.waterFogColor(original); - } - @Inject(method = "getFogModifier(Lnet/minecraft/entity/Entity;F)Lnet/minecraft/client/render/BackgroundRenderer$StatusEffectFogModifier;", at = @At("HEAD"), cancellable = true) - private static void injectFogModifier(Entity entity, float tickProgress, CallbackInfoReturnable cir){ - if (entity instanceof LivingEntity livingEntity) { - Stream modifiers = FOG_MODIFIERS - .stream() - .filter((modifier) -> - modifier.shouldApply(livingEntity, tickProgress) && NoRender.shouldAcceptFog(modifier) - ); - cir.setReturnValue(modifiers.findFirst().orElse(null)); + @Inject(method = "shouldApply", at = @At("HEAD"), cancellable = true) + private void injectShouldApplyBlindness(CameraSubmersionType submersionType, Entity cameraEntity, CallbackInfoReturnable cir) { + if (NoRender.getNoBlindness() && NoRender.isEnabled()) { + cir.setReturnValue(false); } } } diff --git a/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java b/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java new file mode 100644 index 000000000..97aa0bb90 --- /dev/null +++ b/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java @@ -0,0 +1,41 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.mixin.render; + +import com.lambda.module.modules.render.NoRender; +import net.minecraft.client.render.fog.DarknessEffectFogModifier; +import net.minecraft.block.enums.CameraSubmersionType; +import net.minecraft.entity.Entity; +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 to disable darkness fog effect when NoRender is enabled. + */ +@Mixin(DarknessEffectFogModifier.class) +public class DarknessEffectFogMixin { + + @Inject(method = "shouldApply", at = @At("HEAD"), cancellable = true) + private void injectShouldApplyDarkness(CameraSubmersionType submersionType, Entity cameraEntity, CallbackInfoReturnable cir) { + if (NoRender.getNoDarkness() && NoRender.isEnabled()) { + cir.setReturnValue(false); + } + } +} diff --git a/src/main/java/com/lambda/mixin/render/DebugRendererMixin.java b/src/main/java/com/lambda/mixin/render/DebugRendererMixin.java index 3491d7c24..8b703e194 100644 --- a/src/main/java/com/lambda/mixin/render/DebugRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/DebugRendererMixin.java @@ -29,8 +29,8 @@ @Mixin(DebugRenderer.class) public class DebugRendererMixin { - @Inject(method = "render", at = @At("TAIL")) - private void onRender(MatrixStack matrices, Frustum frustum, VertexConsumerProvider.Immediate vertexConsumers, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) { - DebugRendererModule.render(matrices, vertexConsumers, cameraX, cameraY, cameraZ); - } +// @Inject(method = "render", at = @At("TAIL")) +// private void onRender(MatrixStack matrices, Frustum frustum, VertexConsumerProvider.Immediate vertexConsumers, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) { +// DebugRendererModule.render(matrices, vertexConsumers, cameraX, cameraY, cameraZ); +// } } diff --git a/src/main/java/com/lambda/mixin/render/GameRendererMixin.java b/src/main/java/com/lambda/mixin/render/GameRendererMixin.java index 42cb9ace5..a9386af93 100644 --- a/src/main/java/com/lambda/mixin/render/GameRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/GameRendererMixin.java @@ -59,12 +59,12 @@ private void updateTargetedEntityInvoke(float tickDelta, CallbackInfo info) { * this.client.worldRenderer.render(this.pool, renderTickCounter, bl, camera, this, matrix4f3, matrix4f); * } */ - @WrapOperation(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;render(Lnet/minecraft/client/util/ObjectAllocator;Lnet/minecraft/client/render/RenderTickCounter;ZLnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/GameRenderer;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V")) - void onRenderWorld(WorldRenderer instance, ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, Operation original) { - original.call(instance, allocator, tickCounter, renderBlockOutline, camera, gameRenderer, positionMatrix, projectionMatrix); - - RenderMain.render3D(positionMatrix, projectionMatrix); - } +// @WrapOperation(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;render(Lnet/minecraft/client/util/ObjectAllocator;Lnet/minecraft/client/render/RenderTickCounter;ZLnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/GameRenderer;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V")) +// void onRenderWorld(WorldRenderer instance, ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, Operation original) { +// original.call(instance, allocator, tickCounter, renderBlockOutline, camera, gameRenderer, positionMatrix, projectionMatrix); +// +// RenderMain.render3D(positionMatrix, projectionMatrix); +// } @ModifyExpressionValue(method = "renderWorld", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0)) private float modifyMax(float original) { diff --git a/src/main/kotlin/com/lambda/Lambda.kt b/src/main/kotlin/com/lambda/Lambda.kt index c8242977d..d90bbbaad 100644 --- a/src/main/kotlin/com/lambda/Lambda.kt +++ b/src/main/kotlin/com/lambda/Lambda.kt @@ -27,6 +27,7 @@ import com.lambda.config.serializer.ItemCodec import com.lambda.config.serializer.ItemStackCodec import com.lambda.config.serializer.KeyCodeCodec import com.lambda.config.serializer.OptionalCodec +import com.lambda.config.serializer.TextCodec import com.lambda.core.Loader import com.lambda.event.events.ClientEvent import com.lambda.event.listener.UnsafeListener.Companion.listenOnceUnsafe @@ -79,7 +80,7 @@ object Lambda : ClientModInitializer { .registerTypeAdapter(GameProfile::class.java, GameProfileCodec) .registerTypeAdapter(Optional::class.java, OptionalCodec) .registerTypeAdapter(ItemStack::class.java, ItemStackCodec) - .registerTypeAdapter(Text::class.java, Text.Serializer(DynamicRegistryManager.EMPTY)) + .registerTypeAdapter(Text::class.java, TextCodec) // ToDo: Find out if needed .registerTypeAdapter(Item::class.java, ItemCodec) .registerTypeAdapter(BlockItem::class.java, ItemCodec) .registerTypeAdapter(ArrowItem::class.java, ItemCodec) diff --git a/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt index 7c6ef926b..bdaae0d48 100644 --- a/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt +++ b/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt @@ -35,6 +35,7 @@ import net.minecraft.command.argument.EntityArgumentType import net.minecraft.command.argument.GameProfileArgumentType import net.minecraft.command.argument.TeamArgumentType import net.minecraft.scoreboard.Team +import net.minecraft.server.PlayerConfigEntry import net.minecraft.server.command.ServerCommandSource import net.minecraft.server.network.ServerPlayerEntity @@ -69,7 +70,7 @@ fun ArgumentReader< DefaultArgumentDescriptor< GameProfileArgumentType > - >.value(): Collection { + >.value(): Collection { return GameProfileArgumentType.getProfileArgument(context, name) } diff --git a/src/main/kotlin/com/lambda/command/CommandManager.kt b/src/main/kotlin/com/lambda/command/CommandManager.kt index 2344c4259..960d83a92 100644 --- a/src/main/kotlin/com/lambda/command/CommandManager.kt +++ b/src/main/kotlin/com/lambda/command/CommandManager.kt @@ -73,7 +73,7 @@ object CommandManager { canRead() && peek() == prefix } - fun currentDispatcher(message: String): CommandDispatcher { + fun currentDispatcher(message: String): CommandDispatcher { return if (message.isLambdaCommand()) { dispatcher } else { diff --git a/src/main/kotlin/com/lambda/config/ConfigEditor.kt b/src/main/kotlin/com/lambda/config/ConfigEditor.kt index 4a9e168b9..2e74a71b1 100644 --- a/src/main/kotlin/com/lambda/config/ConfigEditor.kt +++ b/src/main/kotlin/com/lambda/config/ConfigEditor.kt @@ -38,11 +38,11 @@ open class SettingGroupEditor(open val c: T) { throw IllegalStateException("Could not access delegate for property $name", e) } - fun KProperty0.setting() = + fun KProperty0.setting() = this.delegate as? Setting, T> ?: throw IllegalStateException("Setting delegate did not match current value's type") - fun KProperty0.settingCore() = setting().core + fun KProperty0.settingCore() = setting().core @SettingEditorDsl inline fun KProperty0.edit(edits: TypedEditBuilder.(SettingCore) -> Unit) { @@ -60,14 +60,14 @@ open class SettingGroupEditor(open val c: T) { fun edit( vararg settings: KProperty0<*>, edits: BasicEditBuilder.() -> Unit - ) = BasicEditBuilder(this, settings.map { it.setting() }).apply(edits) + ) = BasicEditBuilder(this, settings.map { (it as KProperty0).setting() }).apply(edits) @SettingEditorDsl inline fun editWith( vararg settings: KProperty0<*>, other: KProperty0, edits: BasicEditBuilder.(SettingCore) -> Unit - ) = BasicEditBuilder(this, settings.map { it.setting() }).edits(other.settingCore()) + ) = BasicEditBuilder(this, settings.map { (it as KProperty0).setting() }).edits(other.settingCore()) @SettingEditorDsl inline fun editTyped( @@ -89,7 +89,7 @@ open class SettingGroupEditor(open val c: T) { @SettingEditorDsl fun hide(vararg settings: KProperty0<*>) = - hide(settings.map { it.setting() }) + hide(settings.map { (it as KProperty0).setting() }) open class BasicEditBuilder(val c: SettingGroupEditor<*>, open val settings: Collection>) { @SettingEditorDsl @@ -123,8 +123,10 @@ class ConfigurableEditor(override val c: T) : SettingGroupEdit fun hideGroup(settingGroup: ISettingGroup) = hide(settingGroup.settings) @SettingEditorDsl - fun hideGroupExcept(settingGroup: ISettingGroup, vararg except: KProperty0<*>) = - hide(*((settingGroup.settings as List>) - except.toSet()).toTypedArray()) + fun hideGroupExcept(settingGroup: ISettingGroup, vararg except: KProperty0<*>) { + val exceptSettings = except.map { (it as KProperty0).setting() }.toSet() + hide(settingGroup.settings.filter { it !in exceptSettings }) + } @SettingEditorDsl fun hideGroups(vararg settingGroups: ISettingGroup) = diff --git a/src/main/kotlin/com/lambda/config/serializer/TextCodec.kt b/src/main/kotlin/com/lambda/config/serializer/TextCodec.kt new file mode 100644 index 000000000..629e3ebb6 --- /dev/null +++ b/src/main/kotlin/com/lambda/config/serializer/TextCodec.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.config.serializer + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonElement +import com.google.gson.JsonSerializationContext +import com.lambda.config.Codec +import com.mojang.serialization.JsonOps +import net.minecraft.text.Text +import net.minecraft.text.TextCodecs +import java.lang.reflect.Type +import kotlin.jvm.optionals.getOrElse + +object TextCodec : Codec { + override fun serialize( + src: Text, + typeOfSrc: Type, + context: JsonSerializationContext, + ): JsonElement = + TextCodecs.CODEC.encodeStart(JsonOps.INSTANCE, src) + .orThrow + + override fun deserialize( + json: JsonElement?, + typeOfT: Type?, + context: JsonDeserializationContext?, + ): Text = + TextCodecs.CODEC.parse(JsonOps.INSTANCE, json) + .result() + .getOrElse { Text.empty() } +} diff --git a/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 3668298f2..02ff3bca7 100644 --- a/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -49,7 +49,7 @@ object RenderMain { setupGL { val framebuffer = mc.framebuffer val prevFramebuffer = (framebuffer.getColorAttachment() as GlTexture).getOrCreateFramebuffer( - (RenderSystem.getDevice() as GlBackend).framebufferManager, + (RenderSystem.getDevice() as GlBackend).bufferManager, null ) diff --git a/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt b/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt index 271614ff5..c112c4bf9 100644 --- a/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt +++ b/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt @@ -61,7 +61,8 @@ open class AbstractGUIRenderer( shader["u_ShadeColor1"] = ClickGuiLayout.primaryColor shader["u_ShadeColor2"] = ClickGuiLayout.secondaryColor - shader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(ClickGuiLayout.colorWidth, ClickGuiLayout.colorHeight) + // ToDo: GUI Rewrite +// shader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(ClickGuiLayout.colorWidth, ClickGuiLayout.colorHeight) } pipeline.apply { diff --git a/src/main/kotlin/com/lambda/graphics/shader/Shader.kt b/src/main/kotlin/com/lambda/graphics/shader/Shader.kt index 440c98571..41565ea88 100644 --- a/src/main/kotlin/com/lambda/graphics/shader/Shader.kt +++ b/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -51,7 +51,8 @@ class Shader private constructor(name: String) { fun use() { glUseProgram(id) - set("u_ProjModel", RenderMain.projModel) + // ToDo: GUI Rewrite +// set("u_ProjModel", RenderMain.projModel) } private fun loc(name: String) = diff --git a/src/main/kotlin/com/lambda/gui/DearImGui.kt b/src/main/kotlin/com/lambda/gui/DearImGui.kt index 13b4f24fa..66dfda249 100644 --- a/src/main/kotlin/com/lambda/gui/DearImGui.kt +++ b/src/main/kotlin/com/lambda/gui/DearImGui.kt @@ -103,7 +103,7 @@ object DearImGui : Loadable { val framebuffer = mc.framebuffer val prevFramebuffer = (framebuffer.getColorAttachment() as GlTexture).getOrCreateFramebuffer( - (RenderSystem.getDevice() as GlBackend).framebufferManager, + (RenderSystem.getDevice() as GlBackend).bufferManager, null ) diff --git a/src/main/kotlin/com/lambda/gui/MenuBar.kt b/src/main/kotlin/com/lambda/gui/MenuBar.kt index 3856aea6f..bfef5ac0f 100644 --- a/src/main/kotlin/com/lambda/gui/MenuBar.kt +++ b/src/main/kotlin/com/lambda/gui/MenuBar.kt @@ -52,6 +52,11 @@ import imgui.flag.ImGuiCol import imgui.flag.ImGuiStyleVar import imgui.flag.ImGuiWindowFlags import net.fabricmc.loader.api.FabricLoader +import net.minecraft.client.gui.hud.debug.DebugHudEntries +import net.minecraft.client.gui.screen.DebugOptionsScreen +import net.minecraft.command.permission.Permission +import net.minecraft.network.packet.c2s.play.ChangeGameModeC2SPacket +import net.minecraft.server.command.GameModeCommand import net.minecraft.util.Util import net.minecraft.world.GameMode import java.util.* @@ -346,18 +351,18 @@ object MenuBar { } separator() runSafe { - menu("Gamemode", enabled = player.hasPermissionLevel(2)) { + menu("Gamemode", enabled = GameModeCommand.PERMISSION_CHECK.allows(player.permissions)) { menuItem("Survival", selected = interaction.gameMode == GameMode.SURVIVAL) { - connection.sendCommand("gamemode survival") + connection.sendPacket(ChangeGameModeC2SPacket(GameMode.SURVIVAL)) } menuItem("Creative", selected = interaction.gameMode == GameMode.CREATIVE) { - connection.sendCommand("gamemode creative") + connection.sendPacket(ChangeGameModeC2SPacket(GameMode.CREATIVE)) } menuItem("Adventure", selected = interaction.gameMode == GameMode.ADVENTURE) { - connection.sendCommand("gamemode adventure") + connection.sendPacket(ChangeGameModeC2SPacket(GameMode.ADVENTURE)) } menuItem("Spectator", selected = interaction.gameMode == GameMode.SPECTATOR) { - connection.sendCommand("gamemode spectator") + connection.sendPacket(ChangeGameModeC2SPacket(GameMode.SPECTATOR)) } } menu("Debug Menu") { @@ -365,16 +370,6 @@ object MenuBar { mc.options.advancedItemTooltips = !mc.options.advancedItemTooltips mc.options.write() } - menuItem("Show Chunk Borders", "F3+G", mc.debugRenderer.showChunkBorder) { - mc.debugRenderer.toggleShowChunkBorder() - } - menuItem("Show Octree", selected = mc.debugRenderer.showOctree) { - mc.debugRenderer.toggleShowOctree() - } - menuItem("Show Hitboxes", "F3+B", mc.entityRenderDispatcher.shouldRenderHitboxes()) { - val now = !mc.entityRenderDispatcher.shouldRenderHitboxes() - mc.entityRenderDispatcher.setRenderHitboxes(now) - } menuItem("Copy Location (as command)", "F3+C") { val cmd = String.format( Locale.ROOT, @@ -388,6 +383,9 @@ object MenuBar { menuItem("Clear Chat", "F3+D") { mc.inGameHud?.chatHud?.clear(false) } + menuItem("Open Debug Entry Menu", "(new)") { // ToDo: Put actual keybind in + mc.setScreen(DebugOptionsScreen()) + } separator() @@ -414,8 +412,8 @@ object MenuBar { separator() - menuItem("Show Debug Menu", "F3", mc.debugHud.showDebugHud) { - mc.debugHud.toggleDebugHud() + menuItem("Show Debug Menu", "F3", mc.debugHudEntryList.isF3Enabled) { + mc.debugHudEntryList.toggleF3Enabled() } menuItem("Rendering Chart", "F3+1", mc.debugHud.renderingChartVisible) { mc.debugHud.toggleRenderingChart() diff --git a/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt b/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt index b54bad9d4..ced5ec71b 100644 --- a/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt +++ b/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt @@ -115,7 +115,7 @@ object ClickGuiLayout : Loadable, Configurable(GuiConfig) { if (to) { setLambdaWindowIcon() } else { - val icon = if (SharedConstants.getGameVersion().isStable) Icons.RELEASE else Icons.SNAPSHOT + val icon = if (SharedConstants.getGameVersion().stable()) Icons.RELEASE else Icons.SNAPSHOT mc.window.setIcon(mc.defaultResourcePack, icon) } } diff --git a/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt b/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt index 396a9b826..f4815152b 100644 --- a/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt +++ b/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt @@ -91,7 +91,7 @@ object QuickSearch { override fun ImGuiBuilder.buildLayout() { text(command.name.capitalize()) sameLine() - smallButton("Insert") { mc.setScreen(ChatScreen("${CommandRegistry.prefix}${command.name} ")) } + smallButton("Insert") { mc.setScreen(ChatScreen("${CommandRegistry.prefix}${command.name} ", true)) } if (command.description.isNotBlank()) { sameLine() textDisabled(command.description) diff --git a/src/main/kotlin/com/lambda/interaction/PlayerPacketHandler.kt b/src/main/kotlin/com/lambda/interaction/PlayerPacketHandler.kt index 5cda2e509..dca27c164 100644 --- a/src/main/kotlin/com/lambda/interaction/PlayerPacketHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/PlayerPacketHandler.kt @@ -39,7 +39,6 @@ object PlayerPacketHandler { var lastPosition: Vec3d = Vec3d.ZERO var lastRotation = Rotation.ZERO var lastSprint = false - var lastSneak = false var lastOnGround = false var lastHorizontalCollision = false @@ -60,21 +59,6 @@ object PlayerPacketHandler { } } - @JvmStatic - fun sendSneakPackets() { - runSafe { - val sneaking = player.isSneaking - if (sneaking == lastSneak) return@runSafe - val mode = if (sneaking) { - ClientCommandC2SPacket.Mode.PRESS_SHIFT_KEY - } else { - ClientCommandC2SPacket.Mode.RELEASE_SHIFT_KEY - } - connection.sendPacket(ClientCommandC2SPacket(player, mode)) - lastSneak = sneaking - } - } - private fun SafeContext.updatePlayerPackets(new: PlayerPacketEvent.Pre) { configurations.add(new) diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index 98e5f6c4c..cef79a426 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -41,7 +41,7 @@ data class Simulation( private val automated: Automated ) : Automated by automated { private val cache: MutableMap> = mutableMapOf() - private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, ClientPlayerEntity.DEFAULT_EYE_HEIGHT.toDouble(), 0.5) + private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, ClientPlayerEntity.EYE_HEIGHT.toDouble(), 0.5) fun simulate(pos: FastVector) = cache.getOrPut(pos) { diff --git a/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt index 597feefd1..b6c388ba6 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt @@ -92,6 +92,7 @@ import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance +import net.minecraft.client.world.BlockParticleEffectsManager import net.minecraft.entity.ItemEntity import net.minecraft.item.ItemStack import net.minecraft.sound.SoundCategory @@ -631,7 +632,7 @@ object BreakManager : Manager( && tickStage in breakConfig.tickStageMask && (rotated || type != Primary) - if (updatedPreProcessingThisTick) return + if (updatedPreProcessingThisTick) return@runSafeAutomated updatedPreProcessingThisTick = true swapStack = player.inventory.getStack(context.hotbarIndex) @@ -746,7 +747,8 @@ object BreakManager : Manager( } if (breakConfig.particles) { - mc.particleManager.addBlockBreakingParticles(ctx.blockPos, hitResult.side) + // ToDo: Set block breaking info on world.blockBreakingInfo +// mc.particleManager.addBlockBreakingParticles(ctx.blockPos, hitResult.side) } if (breakConfig.breakingTexture) { diff --git a/src/main/kotlin/com/lambda/interaction/managers/rotating/visibilty/PlaceDirection.kt b/src/main/kotlin/com/lambda/interaction/managers/rotating/visibilty/PlaceDirection.kt index eb858d4d0..2a78ca14e 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/rotating/visibilty/PlaceDirection.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/rotating/visibilty/PlaceDirection.kt @@ -22,6 +22,7 @@ import net.minecraft.entity.Entity import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper import net.minecraft.util.math.Vec3i +import kotlin.math.sin enum class PlaceDirection( val rotation: Rotation, @@ -58,8 +59,8 @@ enum class PlaceDirection( * @see Direction.getEntityFacingOrder */ fun fromRotation(rotation: Rotation): PlaceDirection { - val pitchRad = rotation.pitchF * (Math.PI.toFloat() / 180f) - val yawRad = -rotation.yawF * (Math.PI.toFloat() / 180f) + val pitchRad = rotation.pitchF * (Math.PI.toFloat() / 180f).toDouble() + val yawRad = -rotation.yawF * (Math.PI.toFloat() / 180f).toDouble() val sinPitch = MathHelper.sin(pitchRad) val cosPitch = MathHelper.cos(pitchRad) diff --git a/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index 7afdb5a64..7fe6a4779 100644 --- a/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -29,9 +29,12 @@ import com.lambda.threading.onShutdown import com.lambda.util.Timer import com.lambda.util.player.spawnFakePlayer import com.mojang.authlib.GameProfile +import com.mojang.datafixers.util.Either import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry import java.util.* +import kotlin.jvm.optionals.getOrElse +import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration.Companion.seconds object FakePlayer : Module( @@ -76,7 +79,8 @@ object FakePlayer : Module( val requestedProfile = getProfile(user).getOrElse { return nilProfile } // Fetch the skin properties from mojang - val properties = mc.sessionService.fetchProfile(requestedProfile.id, true)?.profile?.properties + val properties = mc.apiServices.profileResolver + .getProfile(Either.right(requestedProfile.id)).getOrNull()?.properties // We use the nil profile to avoid the nil username if something wrong happens // Check the GameProfile deserializer you'll understand diff --git a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index 7586e4707..6a5ece8c5 100644 --- a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -116,7 +116,7 @@ object KillAura : Module( private fun SafeContext.runAttack(target: LivingEntity) { // Cooldown check when (attackMode) { - AttackMode.Cooldown -> if (player.lastAttackedTicks < 1 / player.attackSpeed() * 20 + cooldownOffset) return + AttackMode.Cooldown -> if (player.lastAttackedTime < 1 / player.attackSpeed() * 20 + cooldownOffset) return AttackMode.Delay -> if (System.currentTimeMillis() - lastAttackTime < hitDelay) return } diff --git a/src/main/kotlin/com/lambda/module/modules/debug/DebugRendererModule.kt b/src/main/kotlin/com/lambda/module/modules/debug/DebugRendererModule.kt index 9e8c6bb73..152abaec5 100644 --- a/src/main/kotlin/com/lambda/module/modules/debug/DebugRendererModule.kt +++ b/src/main/kotlin/com/lambda/module/modules/debug/DebugRendererModule.kt @@ -53,35 +53,36 @@ object DebugRendererModule: Module( // private val chunkDebugRenderer by setting("Chunk Debug Renderer", false) // private val octreeDebugRenderer by setting("Octree Debug Renderer", false) - @JvmStatic - fun render( - matrices: MatrixStack, - vertexConsumers: VertexConsumerProvider.Immediate, - cameraX: Double, - cameraY: Double, cameraZ: Double - ) { - val renderers = mc.debugRenderer - mutableListOf().apply { - if (waterDebugRenderer) add(renderers.waterDebugRenderer) - if (chunkBorderDebugRenderer) add(renderers.chunkBorderDebugRenderer) - if (heightmapDebugRenderer) add(renderers.heightmapDebugRenderer) - if (collisionDebugRenderer) add(renderers.collisionDebugRenderer) - if (supportingBlockDebugRenderer) add(renderers.supportingBlockDebugRenderer) - if (neighborUpdateDebugRenderer) add(renderers.neighborUpdateDebugRenderer) - if (redstoneUpdateOrderDebugRenderer) add(renderers.redstoneUpdateOrderDebugRenderer) - if (structureDebugRenderer) add(renderers.structureDebugRenderer) - if (skyLightDebugRenderer) add(renderers.skyLightDebugRenderer) - if (worldGenAttemptDebugRenderer) add(renderers.worldGenAttemptDebugRenderer) - if (blockOutlineDebugRenderer) add(renderers.blockOutlineDebugRenderer) - if (chunkLoadingDebugRenderer) add(renderers.chunkLoadingDebugRenderer) - if (villageDebugRenderer) add(renderers.villageDebugRenderer) - if (villageSectionsDebugRenderer) add(renderers.villageSectionsDebugRenderer) - if (beeDebugRenderer) add(renderers.beeDebugRenderer) - if (raidCenterDebugRenderer) add(renderers.raidCenterDebugRenderer) - if (goalSelectorDebugRenderer) add(renderers.goalSelectorDebugRenderer) - if (gameTestDebugRenderer) add(renderers.gameTestDebugRenderer) - if (gameEventDebugRenderer) add(renderers.gameEventDebugRenderer) - if (lightDebugRenderer) add(renderers.lightDebugRenderer) - }.forEach { it.render(matrices, vertexConsumers, cameraX, cameraY, cameraZ) } - } + // ToDo: Was changed in 1.21.11 -> now we have editable HUD but we may want to add all the hidden options here eg pathfinder +// @JvmStatic +// fun render( +// matrices: MatrixStack, +// vertexConsumers: VertexConsumerProvider.Immediate, +// cameraX: Double, +// cameraY: Double, cameraZ: Double +// ) { +// val renderers = mc.worldRenderer.debugRenderer +// mutableListOf().apply { +// if (waterDebugRenderer) add(renderers.waterDebugRenderer) +// if (chunkBorderDebugRenderer) add(renderers.chunkBorderDebugRenderer) +// if (heightmapDebugRenderer) add(renderers.heightmapDebugRenderer) +// if (collisionDebugRenderer) add(renderers.collisionDebugRenderer) +// if (supportingBlockDebugRenderer) add(renderers.supportingBlockDebugRenderer) +// if (neighborUpdateDebugRenderer) add(renderers.neighborUpdateDebugRenderer) +// if (redstoneUpdateOrderDebugRenderer) add(renderers.redstoneUpdateOrderDebugRenderer) +// if (structureDebugRenderer) add(renderers.structureDebugRenderer) +// if (skyLightDebugRenderer) add(renderers.skyLightDebugRenderer) +// if (worldGenAttemptDebugRenderer) add(renderers.worldGenAttemptDebugRenderer) +// if (blockOutlineDebugRenderer) add(renderers.blockOutlineDebugRenderer) +// if (chunkLoadingDebugRenderer) add(renderers.chunkLoadingDebugRenderer) +// if (villageDebugRenderer) add(renderers.villageDebugRenderer) +// if (villageSectionsDebugRenderer) add(renderers.villageSectionsDebugRenderer) +// if (beeDebugRenderer) add(renderers.beeDebugRenderer) +// if (raidCenterDebugRenderer) add(renderers.raidCenterDebugRenderer) +// if (goalSelectorDebugRenderer) add(renderers.goalSelectorDebugRenderer) +// if (gameTestDebugRenderer) add(renderers.gameTestDebugRenderer) +// if (gameEventDebugRenderer) add(renderers.gameEventDebugRenderer) +// if (lightDebugRenderer) add(renderers.lightDebugRenderer) +// }.forEach { it.render(matrices, vertexConsumers, cameraX, cameraY, cameraZ) } +// } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/module/modules/render/ContainerPreview.kt b/src/main/kotlin/com/lambda/module/modules/render/ContainerPreview.kt index 22c8ee793..03a30fd93 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/ContainerPreview.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/ContainerPreview.kt @@ -29,7 +29,7 @@ import net.minecraft.block.ShulkerBoxBlock import net.minecraft.client.font.TextRenderer import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.tooltip.TooltipComponent -import net.minecraft.client.render.RenderLayer +import net.minecraft.client.gl.RenderPipelines import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack import net.minecraft.item.Items @@ -152,8 +152,7 @@ object ContainerPreview : Module( val height = getTooltipHeight() val matrices = context.matrices - matrices.push() - matrices.translate(0f, 0f, 400f) + matrices.pushMatrix() val tintColor = getContainerTintColor(stack) @@ -201,11 +200,10 @@ object ContainerPreview : Module( } } - matrices.pop() + matrices.popMatrix() hoveredStack?.let { stack -> - matrices.push() - matrices.translate(0f, 0f, 500f) + matrices.pushMatrix() if (isPreviewableContainer(stack)) { val nestedWidth = getTooltipWidth() @@ -221,7 +219,7 @@ object ContainerPreview : Module( isRenderingSubTooltip = false } } - matrices.pop() + matrices.popMatrix() } } @@ -274,11 +272,12 @@ object ContainerPreview : Module( // Bottom: y=160 onwards context.drawTexture( - RenderLayer::getGuiTextured, + RenderPipelines.GUI_TEXTURED, background, x, y, 0f, 0f, width, TITLE_HEIGHT, + width, TITLE_HEIGHT, 256, 256, tintColor ) @@ -286,11 +285,12 @@ object ContainerPreview : Module( // Middle rows (0 until ROWS).forEach { row -> context.drawTexture( - RenderLayer::getGuiTextured, + RenderPipelines.GUI_TEXTURED, background, x, y + TITLE_HEIGHT + row * SLOT_SIZE, 0f, 17f, width, SLOT_SIZE, + width, SLOT_SIZE, 256, 256, tintColor ) @@ -298,11 +298,12 @@ object ContainerPreview : Module( // Bottom context.drawTexture( - RenderLayer::getGuiTextured, + RenderPipelines.GUI_TEXTURED, background, x, y + TITLE_HEIGHT + ROWS * SLOT_SIZE, 0f, 160f, width, PADDING, + width, PADDING, 256, 256, tintColor ) diff --git a/src/main/kotlin/com/lambda/module/modules/render/MapPreview.kt b/src/main/kotlin/com/lambda/module/modules/render/MapPreview.kt index cb17e9d65..667974f8f 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/MapPreview.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/MapPreview.kt @@ -24,7 +24,7 @@ import net.minecraft.client.font.TextRenderer import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.tooltip.TooltipComponent import net.minecraft.client.render.MapRenderState -import net.minecraft.client.render.RenderLayer +import net.minecraft.client.gl.RenderPipelines import net.minecraft.component.DataComponentTypes import net.minecraft.component.type.MapIdComponent import net.minecraft.item.FilledMapItem @@ -56,19 +56,19 @@ object MapPreview : Module( val matrices = context.matrices // Render the map background - matrices.push() - context.drawTexture(RenderLayer::getGuiTextured, background, x, y, 0f, 0f, 64, 64, 64, 64) - matrices.pop() + matrices.pushMatrix() + context.drawTexture(RenderPipelines.GUI_TEXTURED, background, x, y, 0f, 0f, 64, 64, 64, 64, 64, 64) + matrices.popMatrix() // Render the map texture - matrices.push() - matrices.translate(x + 3.2, y + 3.2, 401.0) - matrices.scale(0.45f, 0.45f, 1f) + matrices.pushMatrix() + matrices.translate(x + 3.2f, y + 3.2f) + matrices.scale(0.45f, 0.45f) val renderState = MapRenderState() mc.mapRenderer.update(id, state, renderState) - context.draw { mc.mapRenderer.draw(renderState, matrices, it, true, 0xF000F0) } - matrices.pop() + context.drawMap(renderState) + matrices.popMatrix() } override fun getHeight(textRenderer: TextRenderer) = diff --git a/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt b/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt index e496a768b..70af2f42c 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt @@ -25,10 +25,8 @@ import com.lambda.util.reflections.scanResult import io.github.classgraph.ClassInfo import net.minecraft.block.entity.BlockEntity import net.minecraft.client.particle.Particle -import net.minecraft.client.render.BackgroundRenderer.StatusEffectFogModifier import net.minecraft.entity.Entity import net.minecraft.entity.SpawnGroup -import net.minecraft.entity.effect.StatusEffects //ToDo: Implement unimplemented settings. (Keep in mind compatibility with other mods like sodium) object NoRender : Module( @@ -176,14 +174,6 @@ object NoRender : Module( fun shouldOmitBlockEntity(blockEntity: BlockEntity) = isEnabled && blockEntityMap[blockEntity.javaClass.simpleName] in blockEntities - @JvmStatic - fun shouldAcceptFog(modifier: StatusEffectFogModifier) = - when (modifier.statusEffect) { - StatusEffects.BLINDNESS if (noBlindness && isEnabled) -> false - StatusEffects.DARKNESS if (noDarkness && isEnabled) -> false - else -> true - } - private data class MappingInfo( val raw: String, val remapped: String, diff --git a/src/main/kotlin/com/lambda/network/LambdaAPI.kt b/src/main/kotlin/com/lambda/network/LambdaAPI.kt index f86586403..7ff773e6b 100644 --- a/src/main/kotlin/com/lambda/network/LambdaAPI.kt +++ b/src/main/kotlin/com/lambda/network/LambdaAPI.kt @@ -36,6 +36,7 @@ import net.minecraft.client.network.ClientLoginNetworkHandler import net.minecraft.client.network.ServerAddress import net.minecraft.network.ClientConnection import net.minecraft.network.NetworkSide.CLIENTBOUND +import net.minecraft.network.NetworkingBackend import net.minecraft.network.packet.c2s.login.LoginHelloC2SPacket import net.minecraft.text.Text import java.math.BigInteger @@ -51,9 +52,7 @@ object LambdaAPI : Configurable(LambdaConfig) { val mappings get() = "$assets/mappings" // Folder containing mappings for our dynamic serializer val capes get() = "$assets/capes" // Folder containing all the capes, add .txt to get the list of available capes - - @Suppress("Deprecation") - const val GAME_VERSION = SharedConstants.VERSION_NAME + val gameVersion: String = SharedConstants.getGameVersion().name() private var hash: String? = null @@ -89,10 +88,11 @@ object LambdaAPI : Configurable(LambdaConfig) { val resolved = AllowedAddressResolver.DEFAULT.resolve(address) .map { it.inetSocketAddress }.getOrElse { return } - ClientConnection.connect(resolved, mc.options.shouldUseNativeTransport(), connection) + val backend = NetworkingBackend.remote(mc.options.shouldUseNativeTransport()) + ClientConnection.connect(resolved, backend, connection) .syncUninterruptibly() - val handler = ClientLoginNetworkHandler(connection, mc, null, null, false, null, { Text.empty() }, null) + val handler = ClientLoginNetworkHandler(connection, mc, null, null, false, null, { Text.empty() }, null, null) connection.connect(resolved.hostName, resolved.port, handler) connection.send(LoginHelloC2SPacket(mc.session.username, mc.session.uuidOrNull)) diff --git a/src/main/kotlin/com/lambda/threading/Threading.kt b/src/main/kotlin/com/lambda/threading/Threading.kt index a23a200c6..b1020869c 100644 --- a/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/src/main/kotlin/com/lambda/threading/Threading.kt @@ -106,7 +106,7 @@ inline fun runSafeConcurrent(crossinline block: suspend SafeContext.() -> Unit) * to OpenGL. */ inline fun recordRenderCall(crossinline block: () -> Unit) { - mc.renderTaskQueue.add { block() } + mc.execute { block() } } /** diff --git a/src/main/kotlin/com/lambda/util/BlockUtils.kt b/src/main/kotlin/com/lambda/util/BlockUtils.kt index 4d3cfdbf6..f06e691af 100644 --- a/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -57,7 +57,6 @@ import net.minecraft.block.EnchantingTableBlock import net.minecraft.block.EnderChestBlock import net.minecraft.block.FenceBlock import net.minecraft.block.FenceGateBlock -import net.minecraft.block.FletchingTableBlock import net.minecraft.block.FlowerPotBlock import net.minecraft.block.GrindstoneBlock import net.minecraft.block.HopperBlock @@ -233,7 +232,6 @@ object BlockUtils { EnderChestBlock::class, FenceBlock::class, FenceGateBlock::class, - FletchingTableBlock::class, FlowerPotBlock::class, GrindstoneBlock::class, HopperBlock::class, diff --git a/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt b/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt index 9164bed03..f80564603 100644 --- a/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt +++ b/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt @@ -80,8 +80,8 @@ object DynamicReflectionSerializer : Loadable { private const val INDENT = 2 private val qualifiedMappings = runBlocking { - cache.resolveFile(LambdaAPI.GAME_VERSION) - .downloadIfNotPresent("${LambdaAPI.mappings}/${LambdaAPI.GAME_VERSION}") + cache.resolveFile(LambdaAPI.gameVersion) + .downloadIfNotPresent("${LambdaAPI.mappings}/${LambdaAPI.gameVersion}") .map(::buildMappingsMap) .getOrElse { LOG.error("Unable to download simplified deobfuscated qualifiers", it) diff --git a/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt b/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt index 3913cad33..69bfb79dc 100644 --- a/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt +++ b/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt @@ -33,7 +33,7 @@ val SafeContext.gamemode: GameMode get() = interaction.currentGameMode fun SafeContext.copyPlayer(entity: ClientPlayerEntity) = - ClientPlayerEntity(mc, world, mc.networkHandler, null, null, entity.isSneaking, entity.isSprinting).apply { + ClientPlayerEntity(mc, world, mc.networkHandler, null, null, entity.lastPlayerInput, entity.isSprinting).apply { setPos(entity.x, entity.y, entity.z) setExperience(entity.experienceProgress, entity.totalExperience, entity.experienceLevel) health = entity.health diff --git a/src/main/kotlin/com/lambda/util/text/StyleDsl.kt b/src/main/kotlin/com/lambda/util/text/StyleDsl.kt index 91fa10f20..7c1027e98 100644 --- a/src/main/kotlin/com/lambda/util/text/StyleDsl.kt +++ b/src/main/kotlin/com/lambda/util/text/StyleDsl.kt @@ -23,8 +23,8 @@ import net.minecraft.text.ClickEvent import net.minecraft.text.HoverEvent import net.minecraft.text.MutableText import net.minecraft.text.Style +import net.minecraft.text.StyleSpriteSource import net.minecraft.text.TextColor -import net.minecraft.util.Identifier import java.awt.Color /** @@ -149,9 +149,9 @@ class StyleBuilder { } /** - * An [Identifier] for the Minecraft font that would like to be used. + * A [StyleSpriteSource] for the Minecraft font that would like to be used. */ - var font: Identifier? = null + var font: StyleSpriteSource? = null set(value) { if (field != value) { cachedStyle = null diff --git a/src/main/kotlin/com/lambda/util/text/TextDsl.kt b/src/main/kotlin/com/lambda/util/text/TextDsl.kt index cbf885c13..6a8f3892e 100644 --- a/src/main/kotlin/com/lambda/util/text/TextDsl.kt +++ b/src/main/kotlin/com/lambda/util/text/TextDsl.kt @@ -23,8 +23,8 @@ import net.minecraft.text.HoverEvent import net.minecraft.text.MutableText import net.minecraft.text.NbtDataSource import net.minecraft.text.Style +import net.minecraft.text.StyleSpriteSource import net.minecraft.text.Text -import net.minecraft.util.Identifier import java.awt.Color import java.util.* @@ -296,7 +296,7 @@ inline fun TextBuilder.insertion(insertion: String?, action: TextBuilder.() -> U * Applies the [TextBuilder] [action] with [font] set to the provided value. */ @TextDsl -inline fun TextBuilder.font(font: Identifier?, action: TextBuilder.() -> Unit) { +inline fun TextBuilder.font(font: StyleSpriteSource?, action: TextBuilder.() -> Unit) { withProp(font, { this.font }, { this.font = it }, action) } @@ -314,7 +314,7 @@ fun TextBuilder.styled( clickEvent: ClickEvent? = style.clickEvent, hoverEvent: HoverEvent? = style.hoverEvent, insertion: String? = style.insertion, - font: Identifier? = style.font, + font: StyleSpriteSource? = style.font, action: TextBuilder.() -> Unit, ) { color(color) { diff --git a/src/main/resources/lambda.accesswidener b/src/main/resources/lambda.accesswidener index 1914bc8c9..b1ab3eb4c 100644 --- a/src/main/resources/lambda.accesswidener +++ b/src/main/resources/lambda.accesswidener @@ -6,7 +6,7 @@ transitive-accessible field net/minecraft/client/MinecraftClient paused Z transitive-accessible field net/minecraft/client/MinecraftClient thread Ljava/lang/Thread; transitive-accessible field net/minecraft/client/MinecraftClient uptimeInTicks J transitive-accessible field net/minecraft/client/input/Input movementVector Lnet/minecraft/util/math/Vec2f; -transitive-accessible field net/minecraft/client/MinecraftClient renderTaskQueue Ljava/util/Queue; +# REMOVED in 1.21.11: renderTaskQueue - use mc.execute {} instead (inherited from ReentrantThreadExecutor) transitive-accessible field net/minecraft/client/option/KeyBinding boundKey Lnet/minecraft/client/util/InputUtil$Key; transitive-accessible method net/minecraft/client/MinecraftClient getWindowTitle ()Ljava/lang/String; transitive-accessible field net/minecraft/client/option/KeyBinding KEYS_BY_ID Ljava/util/Map; @@ -26,13 +26,15 @@ transitive-accessible method net/minecraft/item/BlockItem placeFromNbt (Lnet/min transitive-accessible method net/minecraft/item/BlockItem postPlacement (Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Z transitive-accessible method net/minecraft/item/BlockItem getPlaceSound (Lnet/minecraft/block/BlockState;)Lnet/minecraft/sound/SoundEvent; transitive-accessible field net/minecraft/state/State owner Ljava/lang/Object; -transitive-accessible class net/minecraft/client/render/BackgroundRenderer$StatusEffectFogModifier +# MOVED in 1.21.11: BackgroundRenderer$StatusEffectFogModifier -> net/minecraft/client/render/fog/StatusEffectFogModifier +transitive-accessible class net/minecraft/client/render/fog/StatusEffectFogModifier # Entity transitive-accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; transitive-accessible method net/minecraft/entity/Entity movementInputToVelocity (Lnet/minecraft/util/math/Vec3d;FF)Lnet/minecraft/util/math/Vec3d; transitive-accessible method net/minecraft/entity/passive/AbstractHorseEntity setHorseFlag (IZ)V -transitive-accessible field net/minecraft/entity/LivingEntity lastAttackedTicks I +# RENAMED in 1.21.11: lastAttackedTicks -> lastAttackedTime +transitive-accessible field net/minecraft/entity/LivingEntity lastAttackedTime I transitive-accessible method net/minecraft/entity/Entity setFlag (IZ)V transitive-accessible field net/minecraft/client/network/AbstractClientPlayerEntity playerListEntry Lnet/minecraft/client/network/PlayerListEntry; transitive-accessible field net/minecraft/entity/LivingEntity jumpingCooldown I @@ -50,10 +52,12 @@ transitive-accessible method net/minecraft/util/math/Direction listClosest (Lnet transitive-accessible field net/minecraft/client/network/ClientPlayerInteractionManager gameMode Lnet/minecraft/world/GameMode; transitive-accessible method net/minecraft/entity/player/PlayerEntity updatePose ()V transitive-accessible field net/minecraft/screen/ScreenHandler revision I +accessible field net/minecraft/entity/Entity world Lnet/minecraft/world/World; # Camera transitive-accessible method net/minecraft/client/render/Camera setPos (DDD)V transitive-accessible method net/minecraft/client/render/Camera setRotation (FF)V +accessible field net/minecraft/client/render/Camera pos Lnet/minecraft/util/math/Vec3d; # Renderer transitive-accessible field net/minecraft/client/texture/NativeImage pointer J @@ -71,8 +75,10 @@ transitive-accessible field net/minecraft/text/Style obfuscated Ljava/lang/Boole transitive-accessible field net/minecraft/text/Style clickEvent Lnet/minecraft/text/ClickEvent; transitive-accessible field net/minecraft/text/Style hoverEvent Lnet/minecraft/text/HoverEvent; transitive-accessible field net/minecraft/text/Style insertion Ljava/lang/String; -transitive-accessible field net/minecraft/text/Style font Lnet/minecraft/util/Identifier; -transitive-accessible method net/minecraft/text/Style (Lnet/minecraft/text/TextColor;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lnet/minecraft/text/ClickEvent;Lnet/minecraft/text/HoverEvent;Ljava/lang/String;Lnet/minecraft/util/Identifier;)V +# CHANGED in 1.21.11: font type changed from Identifier to StyleSpriteSource +transitive-accessible field net/minecraft/text/Style font Lnet/minecraft/text/StyleSpriteSource; +# CHANGED in 1.21.11: constructor added shadowColor parameter, font type changed to StyleSpriteSource +transitive-accessible method net/minecraft/text/Style (Lnet/minecraft/text/TextColor;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lnet/minecraft/text/ClickEvent;Lnet/minecraft/text/HoverEvent;Ljava/lang/String;Lnet/minecraft/text/StyleSpriteSource;)V # Network transitive-accessible field net/minecraft/client/network/ClientPlayNetworkHandler playerListEntries Ljava/util/Map; @@ -98,12 +104,11 @@ transitive-accessible method net/minecraft/util/math/Vec3i setY (I)Lnet/minecraf transitive-accessible method net/minecraft/util/math/Vec3i setZ (I)Lnet/minecraft/util/math/Vec3i; # Debug -transitive-accessible field net/minecraft/client/gui/hud/DebugHud showDebugHud Z +# REMOVED in 1.21.11: showDebugHud - use DebugHud.shouldShowDebugHud() method instead transitive-accessible field net/minecraft/client/gui/hud/DebugHud renderingChartVisible Z transitive-accessible field net/minecraft/client/gui/hud/DebugHud renderingAndTickChartsVisible Z transitive-accessible field net/minecraft/client/gui/hud/DebugHud packetSizeAndPingChartsVisible Z -transitive-accessible field net/minecraft/client/render/debug/DebugRenderer showChunkBorder Z -transitive-accessible field net/minecraft/client/render/debug/DebugRenderer showOctree Z +# REMOVED in 1.21.11: showChunkBorder/showOctree - now controlled via debugHudEntryList.isEntryVisible(DebugHudEntries.CHUNK_BORDERS/CHUNK_SECTION_OCTREE) # Other transitive-accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; diff --git a/src/main/resources/lambda.mixins.common.json b/src/main/resources/lambda.mixins.common.json index f11bdfb07..1334a327f 100644 --- a/src/main/resources/lambda.mixins.common.json +++ b/src/main/resources/lambda.mixins.common.json @@ -33,6 +33,7 @@ "render.AbstractTerrainRenderContextMixin", "render.ArmorFeatureRendererMixin", "render.BackgroundRendererMixin", + "render.DarknessEffectFogMixin", "render.BeaconBlockEntityRendererMixin", "render.BlockEntityRenderDispatcherMixin", "render.BlockMixin", From 32ef6b118a1901fddba4de4fa038b4175a2baf0d Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 17 Dec 2025 03:38:57 +0100 Subject: [PATCH 02/20] Migrated mixins --- build.gradle.kts | 2 + .../lambda/mixin/input/KeyBindingMixin.java | 2 +- .../com/lambda/mixin/input/KeyboardMixin.java | 26 ++++++++---- .../ClientPlayNetworkHandlerMixin.java | 14 ++++--- .../AbstractTerrainRenderContextMixin.java | 1 - .../mixin/render/BackgroundRendererMixin.java | 2 +- .../BlockEntityRenderDispatcherMixin.java | 29 +++++++++----- .../render/CapeFeatureRendererMixin.java | 21 +++++++--- .../mixin/render/ChatInputSuggestorMixin.java | 3 +- .../mixin/render/DarknessEffectFogMixin.java | 2 +- .../render/ElytraFeatureRendererMixin.java | 40 +++++++++++-------- .../render/LightmapTextureManagerMixin.java | 14 +++++-- .../mixin/render/RenderLayersMixin.java | 20 ++++++---- 13 files changed, 114 insertions(+), 62 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c50958bc1..b62e11c39 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -149,6 +149,8 @@ dependencies { // Fabric modImplementation("net.fabricmc:fabric-loader:$fabricLoaderVersion") modImplementation("net.fabricmc.fabric-api:fabric-api:$fabricApiVersion+$minecraftVersion") + // Explicit dependency on fabric-renderer-indigo for mixin access to internal classes + modCompileOnly("net.fabricmc.fabric-api:fabric-renderer-indigo:4.1.4+1af5c5a75f") modImplementation("net.fabricmc:fabric-language-kotlin:$kotlinFabricVersion.$kotlinVersion") // Add dependencies on the required Kotlin modules. diff --git a/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java b/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java index 87b8a524c..1ba526d2a 100644 --- a/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java +++ b/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java @@ -32,7 +32,7 @@ public class KeyBindingMixin { @ModifyReturnValue(method = "isPressed", at = @At("RETURN")) boolean modifyIsPressed(boolean original) { KeyBinding instance = (KeyBinding) (Object) this; - if (!Objects.equals(instance.getTranslationKey(), "key.sprint")) return original; + if (!Objects.equals(instance.getId(), "key.sprint")) return original; if (Sprint.INSTANCE.isEnabled()) return true; if (Speed.INSTANCE.isEnabled() && Speed.getMode() == Speed.Mode.GrimStrafe) return true; diff --git a/src/main/java/com/lambda/mixin/input/KeyboardMixin.java b/src/main/java/com/lambda/mixin/input/KeyboardMixin.java index 3c2368116..027a3edea 100644 --- a/src/main/java/com/lambda/mixin/input/KeyboardMixin.java +++ b/src/main/java/com/lambda/mixin/input/KeyboardMixin.java @@ -23,6 +23,8 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.client.Keyboard; +import net.minecraft.client.input.CharInput; +import net.minecraft.client.input.KeyInput; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import org.spongepowered.asm.mixin.Mixin; @@ -30,28 +32,36 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +/** + * Mixin to intercept keyboard input events. + * + * Note: In 1.21.11, onKey/onChar methods were refactored to use KeyInput/CharInput records. + * - onKey(long window, int action, KeyInput input) where KeyInput has key, scancode, modifiers + * - onChar(long window, CharInput input) where CharInput has codepoint, modifiers + */ @Mixin(Keyboard.class) public class KeyboardMixin { @WrapMethod(method = "onKey") - private void onKey(long window, int key, int scancode, int action, int modifiers, Operation original) { - EventFlow.post(new KeyboardEvent.Press(key, scancode, action, modifiers)); - original.call(window, key, scancode, action, modifiers); + private void onKey(long window, int action, KeyInput input, Operation original) { + EventFlow.post(new KeyboardEvent.Press(input.key(), input.scancode(), action, input.modifiers())); + original.call(window, action, input); } @Inject(method = "onKey", at = @At("RETURN")) - private void onKeyTail(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { + private void onKeyTail(long window, int action, KeyInput input, CallbackInfo ci) { + int key = input.key(); if (!InventoryMove.getShouldMove() || !InventoryMove.isKeyMovementRelated(key)) return; - InputUtil.Key fromCode = InputUtil.fromKeyCode(key, scancode); + InputUtil.Key fromCode = InputUtil.fromKeyCode(input); KeyBinding.setKeyPressed(fromCode, action != 0); } @WrapMethod(method = "onChar") - private void onChar(long window, int codePoint, int modifiers, Operation original) { - char[] chars = Character.toChars(codePoint); + private void onChar(long window, CharInput input, Operation original) { + char[] chars = Character.toChars(input.codepoint()); for (char c : chars) EventFlow.post(new KeyboardEvent.Char(c)); - original.call(window, codePoint, modifiers); + original.call(window, input); } } diff --git a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java index 49da330b8..fb76f541a 100644 --- a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java @@ -45,8 +45,8 @@ void injectJoinPacket(GameJoinS2CPacket packet, CallbackInfo ci) { void injectPlayerList(PlayerListS2CPacket.Action action, PlayerListS2CPacket.Entry receivedEntry, PlayerListEntry currentEntry, CallbackInfo ci) { if (action != PlayerListS2CPacket.Action.UPDATE_LISTED) return; - var name = currentEntry.getProfile().getName(); - var uuid = currentEntry.getProfile().getId(); + var name = currentEntry.getProfile().name(); + var uuid = currentEntry.getProfile().id(); if (receivedEntry.listed()) { EventFlow.post(new WorldEvent.Player.Join(name, uuid, currentEntry)); @@ -64,17 +64,19 @@ private void onScreenHandlerSlotUpdate(ScreenHandlerSlotUpdateS2CPacket packet, } /** - * Sets displayedUnsecureChatWarning to {@link NoRender#getNoChatVerificationToast()} + * Sets seenInsecureChatWarning to {@link NoRender#getNoChatVerificationToast()} *
{@code
      * this.secureChatEnforced = packet.enforcesSecureChat();
-     * if (this.serverInfo != null && !this.displayedUnsecureChatWarning && !this.isSecureChatEnforced()) {
+     * if (this.serverInfo != null && !this.seenInsecureChatWarning && !this.isSecureChatEnforced()) {
      * SystemToast systemToast = SystemToast.create(this.client, SystemToast.Type.UNSECURE_SERVER_WARNING, UNSECURE_SERVER_TOAST_TITLE, UNSECURE_SERVER_TOAST_TEXT);
      * this.client.getToastManager().add(systemToast);
-     * this.displayedUnsecureChatWarning = true;
+     * this.seenInsecureChatWarning = true;
      * }
      * }
+ * + * Note: In 1.21.11, displayedUnsecureChatWarning was renamed to seenInsecureChatWarning. */ - @ModifyExpressionValue(method = "onGameJoin(Lnet/minecraft/network/packet/s2c/play/GameJoinS2CPacket;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;displayedUnsecureChatWarning:Z", ordinal = 0)) + @ModifyExpressionValue(method = "onGameJoin(Lnet/minecraft/network/packet/s2c/play/GameJoinS2CPacket;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;seenInsecureChatWarning:Z", ordinal = 0)) public boolean onServerMetadata(boolean original) { return (NoRender.getNoChatVerificationToast() && NoRender.INSTANCE.isEnabled()) || original; } diff --git a/src/main/java/com/lambda/mixin/render/AbstractTerrainRenderContextMixin.java b/src/main/java/com/lambda/mixin/render/AbstractTerrainRenderContextMixin.java index f09e674bc..f19cda820 100644 --- a/src/main/java/com/lambda/mixin/render/AbstractTerrainRenderContextMixin.java +++ b/src/main/java/com/lambda/mixin/render/AbstractTerrainRenderContextMixin.java @@ -28,7 +28,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@SuppressWarnings("UnstableApiUsage") @Mixin(AbstractTerrainRenderContext.class) public class AbstractTerrainRenderContextMixin { @Final diff --git a/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java b/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java index b396db1b5..9fe12e8af 100644 --- a/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java @@ -38,7 +38,7 @@ public class BackgroundRendererMixin { @Inject(method = "shouldApply", at = @At("HEAD"), cancellable = true) private void injectShouldApplyBlindness(CameraSubmersionType submersionType, Entity cameraEntity, CallbackInfoReturnable cir) { - if (NoRender.getNoBlindness() && NoRender.isEnabled()) { + if (NoRender.INSTANCE.getNoBlindness() && NoRender.INSTANCE.isEnabled()) { cir.setReturnValue(false); } } diff --git a/src/main/java/com/lambda/mixin/render/BlockEntityRenderDispatcherMixin.java b/src/main/java/com/lambda/mixin/render/BlockEntityRenderDispatcherMixin.java index 268b07043..06e6a92b5 100644 --- a/src/main/java/com/lambda/mixin/render/BlockEntityRenderDispatcherMixin.java +++ b/src/main/java/com/lambda/mixin/render/BlockEntityRenderDispatcherMixin.java @@ -19,20 +19,29 @@ import com.lambda.module.modules.render.NoRender; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; -import net.minecraft.client.render.block.entity.BlockEntityRenderer; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.math.Vec3d; +import net.minecraft.client.render.block.entity.BlockEntityRenderManager; +import net.minecraft.client.render.block.entity.state.BlockEntityRenderState; +import net.minecraft.client.render.command.ModelCommandRenderer; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.jspecify.annotations.Nullable; -@Mixin(BlockEntityRenderDispatcher.class) +/** + * Mixin to disable block entity rendering when NoRender is enabled. + * + * Note: In 1.21.11, BlockEntityRenderDispatcher was renamed to BlockEntityRenderManager + * and uses a render state system. Returning null from getRenderState prevents rendering. + */ +@Mixin(BlockEntityRenderManager.class) public class BlockEntityRenderDispatcherMixin { - @Inject(method = "render(Lnet/minecraft/client/render/block/entity/BlockEntityRenderer;Lnet/minecraft/block/entity/BlockEntity;FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/util/math/Vec3d;)V", at = @At("HEAD"), cancellable = true) - private static void injectRender(BlockEntityRenderer renderer, BlockEntity blockEntity, float tickProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, Vec3d cameraPos, CallbackInfo ci) { - if (NoRender.shouldOmitBlockEntity(blockEntity)) ci.cancel(); + @Inject(method = "getRenderState", at = @At("HEAD"), cancellable = true) + private void injectGetRenderState( + E blockEntity, float tickProgress, ModelCommandRenderer.@Nullable CrumblingOverlayCommand crumblingOverlay, + CallbackInfoReturnable cir) { + if (NoRender.shouldOmitBlockEntity(blockEntity)) { + cir.setReturnValue(null); + } } } diff --git a/src/main/java/com/lambda/mixin/render/CapeFeatureRendererMixin.java b/src/main/java/com/lambda/mixin/render/CapeFeatureRendererMixin.java index c1a319f3d..07be67e4a 100644 --- a/src/main/java/com/lambda/mixin/render/CapeFeatureRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/CapeFeatureRendererMixin.java @@ -21,7 +21,7 @@ import com.lambda.module.modules.client.Capes; import com.lambda.network.CapeManager; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.feature.CapeFeatureRenderer; import net.minecraft.client.render.entity.state.PlayerEntityRenderState; import net.minecraft.client.util.math.MatrixStack; @@ -29,16 +29,25 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +/** + * Mixin to override cape textures with Lambda capes. + * + * Note: In 1.21.11, render method uses OrderedRenderCommandQueue instead of VertexConsumerProvider. + * Cape texture is now accessed via skinTextures.cape().texturePath() instead of capeTexture(). + */ @Mixin(CapeFeatureRenderer.class) public class CapeFeatureRendererMixin { - @ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/PlayerEntityRenderState;FF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/SkinTextures;capeTexture()Lnet/minecraft/util/Identifier;")) - Identifier renderCape(Identifier original, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, PlayerEntityRenderState player, float f, float g) { - var entry = Lambda.getMc().getNetworkHandler().getPlayerListEntry(player.name); // this will cause issues if we try to render while not in game + @ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;ILnet/minecraft/client/render/entity/state/PlayerEntityRenderState;FF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/AssetInfo$TextureAsset;texturePath()Lnet/minecraft/util/Identifier;")) + Identifier renderCape(Identifier original, MatrixStack matrixStack, OrderedRenderCommandQueue commandQueue, int i, PlayerEntityRenderState player, float f, float g) { + var networkHandler = Lambda.getMc().getNetworkHandler(); + if (networkHandler == null) return original; + + var entry = player.playerName != null ? networkHandler.getPlayerListEntry(player.playerName.getString()) : null; if (entry == null) return original; var profile = entry.getProfile(); - if (!Capes.INSTANCE.isEnabled() || !CapeManager.INSTANCE.getCache().containsKey(profile.getId())) return original; + if (!Capes.INSTANCE.isEnabled() || !CapeManager.INSTANCE.getCache().containsKey(profile.id())) return original; - return Identifier.of("lambda", CapeManager.INSTANCE.getCache().get(profile.getId())); + return Identifier.of("lambda", CapeManager.INSTANCE.getCache().get(profile.id())); } } diff --git a/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java b/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java index ae9578474..01966adc2 100644 --- a/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java +++ b/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java @@ -62,9 +62,10 @@ private boolean refreshModify(boolean showCompletions) { return CommandManager.INSTANCE.isCommand(textField.getText()); } + @SuppressWarnings("unchecked") @WrapOperation(method = "refresh", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;getCommandDispatcher()Lcom/mojang/brigadier/CommandDispatcher;")) private CommandDispatcher wrapRefresh(ClientPlayNetworkHandler instance, Operation> original) { - return CommandManager.INSTANCE.currentDispatcher(textField.getText()); + return (CommandDispatcher) (Object) CommandManager.INSTANCE.currentDispatcher(textField.getText()); } @Inject(method = "refresh", at = @At("TAIL")) diff --git a/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java b/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java index 97aa0bb90..72e10a19d 100644 --- a/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java +++ b/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java @@ -34,7 +34,7 @@ public class DarknessEffectFogMixin { @Inject(method = "shouldApply", at = @At("HEAD"), cancellable = true) private void injectShouldApplyDarkness(CameraSubmersionType submersionType, Entity cameraEntity, CallbackInfoReturnable cir) { - if (NoRender.getNoDarkness() && NoRender.isEnabled()) { + if (NoRender.INSTANCE.getNoDarkness() && NoRender.INSTANCE.isEnabled()) { cir.setReturnValue(false); } } diff --git a/src/main/java/com/lambda/mixin/render/ElytraFeatureRendererMixin.java b/src/main/java/com/lambda/mixin/render/ElytraFeatureRendererMixin.java index 78a1bdf2c..72dc2c384 100644 --- a/src/main/java/com/lambda/mixin/render/ElytraFeatureRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/ElytraFeatureRendererMixin.java @@ -21,40 +21,48 @@ import com.lambda.module.modules.client.Capes; import com.lambda.module.modules.render.NoRender; import com.lambda.network.CapeManager; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.feature.ElytraFeatureRenderer; import net.minecraft.client.render.entity.state.BipedEntityRenderState; import net.minecraft.client.render.entity.state.PlayerEntityRenderState; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.entity.LivingEntity; import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +/** + * Mixin to override elytra textures with Lambda capes and disable elytra rendering. + * + * Note: In 1.21.11, render method uses OrderedRenderCommandQueue instead of VertexConsumerProvider. + * getTexture is now a private static method. + */ @Mixin(ElytraFeatureRenderer.class) -public class ElytraFeatureRendererMixin { - @WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/BipedEntityRenderState;FF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/feature/ElytraFeatureRenderer;getTexture(Lnet/minecraft/client/render/entity/state/BipedEntityRenderState;)Lnet/minecraft/util/Identifier;")) - Identifier injectElytra(BipedEntityRenderState state, Operation original) { - if (!(state instanceof PlayerEntityRenderState)) - return original.call(state); +public class ElytraFeatureRendererMixin { + @ModifyReturnValue(method = "getTexture", at = @At("RETURN")) + private static Identifier injectElytra(Identifier original, BipedEntityRenderState state) { + if (!(state instanceof PlayerEntityRenderState playerState)) + return original; + + var networkHandler = Lambda.getMc().getNetworkHandler(); + if (networkHandler == null) return original; - var entry = Lambda.getMc().getNetworkHandler().getPlayerListEntry(((PlayerEntityRenderState) state).name); - if (entry == null) return original.call(state); + var entry = playerState.playerName != null ? networkHandler.getPlayerListEntry(playerState.playerName.getString()) : null; + if (entry == null) return original; var profile = entry.getProfile(); - if (!Capes.INSTANCE.isEnabled() || !CapeManager.INSTANCE.getCache().containsKey(profile.getId())) - return original.call(state); + if (!Capes.INSTANCE.isEnabled() || !CapeManager.INSTANCE.getCache().containsKey(profile.id())) + return original; - return Identifier.of("lambda", CapeManager.INSTANCE.getCache().get(profile.getId())); + return Identifier.of("lambda", CapeManager.INSTANCE.getCache().get(profile.id())); } - @WrapMethod(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/BipedEntityRenderState;FF)V") - private void injectRender(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, BipedEntityRenderState bipedEntityRenderState, float f, float g, Operation original) { + @WrapMethod(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;ILnet/minecraft/client/render/entity/state/BipedEntityRenderState;FF)V") + private void injectRender(MatrixStack matrixStack, OrderedRenderCommandQueue commandQueue, int i, BipedEntityRenderState bipedEntityRenderState, float f, float g, Operation original) { if (NoRender.INSTANCE.isDisabled() || !NoRender.getNoElytra()) - original.call(matrixStack, vertexConsumerProvider, i, bipedEntityRenderState, f, g); + original.call(matrixStack, commandQueue, i, bipedEntityRenderState, f, g); } } diff --git a/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java b/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java index 849a9b297..e8cddbbd2 100644 --- a/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java +++ b/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java @@ -35,20 +35,26 @@ import java.util.OptionalInt; +/** + * Mixin to override lightmap for Fullbright/XRay and disable darkness effect. + * + * Note: In 1.21.11, the lightmap rendering was rewritten to use RenderPass with shaders. + * We override the texture after normal rendering completes. + */ @Mixin(LightmapTextureManager.class) public class LightmapTextureManagerMixin { @Shadow @Final private GpuTexture glTexture; @Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;pop()V", shift = At.Shift.BEFORE)) - private void injectUpdate(CallbackInfo ci) { + private void injectUpdate(float tickProgress, CallbackInfo ci) { if (Fullbright.INSTANCE.isEnabled() || XRay.INSTANCE.isEnabled()) { - RenderSystem.getDevice().createCommandEncoder().createRenderPass(glTexture, OptionalInt.of(ColorHelper.getArgb(255, 255, 255, 255))).close(); + RenderSystem.getDevice().createCommandEncoder().clearColorTexture(glTexture, ColorHelper.fullAlpha(ColorHelper.getWhite(1.0f))); } } @ModifyReturnValue(method = "getDarkness", at = @At("RETURN")) - private float modifyGetDarkness(float original) { - if (NoRender.getNoDarkness() && NoRender.INSTANCE.isEnabled()) return 0.0f; + private float modifyGetDarkness(float original, LivingEntity entity, float factor, float tickProgress) { + if (NoRender.INSTANCE.getNoDarkness() && NoRender.INSTANCE.isEnabled()) return 0.0f; return original; } } diff --git a/src/main/java/com/lambda/mixin/render/RenderLayersMixin.java b/src/main/java/com/lambda/mixin/render/RenderLayersMixin.java index 0b4d622dd..96e786e33 100644 --- a/src/main/java/com/lambda/mixin/render/RenderLayersMixin.java +++ b/src/main/java/com/lambda/mixin/render/RenderLayersMixin.java @@ -19,28 +19,34 @@ import com.lambda.module.modules.render.XRay; import net.minecraft.block.BlockState; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.render.RenderLayers; +import net.minecraft.client.render.BlockRenderLayer; +import net.minecraft.client.render.BlockRenderLayers; import net.minecraft.fluid.FluidState; 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(RenderLayers.class) +/** + * Mixin to make blocks render as translucent for XRay functionality. + * + * Note: In 1.21.11, RenderLayers was split - BlockRenderLayers now handles + * block/fluid layer determination and returns BlockRenderLayer enum instead of RenderLayer. + */ +@Mixin(BlockRenderLayers.class) public class RenderLayersMixin { @Inject(method = "getBlockLayer", at = @At("HEAD"), cancellable = true) - private static void injectGetBlockLayer(BlockState state, CallbackInfoReturnable cir) { + private static void injectGetBlockLayer(BlockState state, CallbackInfoReturnable cir) { if (XRay.INSTANCE.isDisabled()) return; final var opacity = XRay.getOpacity(); if (opacity <= 0 || opacity >= 100) return; - if (!XRay.isSelected(state)) cir.setReturnValue(RenderLayer.getTranslucent()); + if (!XRay.isSelected(state)) cir.setReturnValue(BlockRenderLayer.TRANSLUCENT); } @Inject(method = "getFluidLayer", at = @At("HEAD"), cancellable = true) - private static void injectGetFluidLayer(FluidState state, CallbackInfoReturnable cir) { + private static void injectGetFluidLayer(FluidState state, CallbackInfoReturnable cir) { if (XRay.INSTANCE.isDisabled()) return; final var opacity = XRay.getOpacity(); - if (opacity > 0 && opacity < 100) cir.setReturnValue(RenderLayer.getTranslucent()); + if (opacity > 0 && opacity < 100) cir.setReturnValue(BlockRenderLayer.TRANSLUCENT); } } From 20f155a7d18dbe8216f4d8957192e040a1ab4c50 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:21:27 +0000 Subject: [PATCH 03/20] spawnBlockBreakingParticles --- .../com/lambda/interaction/managers/breaking/BreakManager.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt index b6c388ba6..dedd3433e 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt @@ -92,7 +92,6 @@ import com.lambda.util.player.swingHand import net.minecraft.block.BlockState import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.sound.SoundInstance -import net.minecraft.client.world.BlockParticleEffectsManager import net.minecraft.entity.ItemEntity import net.minecraft.item.ItemStack import net.minecraft.sound.SoundCategory @@ -747,8 +746,7 @@ object BreakManager : Manager( } if (breakConfig.particles) { - // ToDo: Set block breaking info on world.blockBreakingInfo -// mc.particleManager.addBlockBreakingParticles(ctx.blockPos, hitResult.side) + world.spawnBlockBreakingParticle(ctx.blockPos, hitResult.side) } if (breakConfig.breakingTexture) { From a05bb881c37ece87917fc1baf9268e7e254f073f Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 17 Dec 2025 04:58:19 +0100 Subject: [PATCH 04/20] Baritone Soft Dependency and even more mixins --- build.gradle.kts | 2 +- .../mixin/client/sound/SoundSystemMixin.java | 7 +++-- .../ClientPlayInteractionManagerMixin.java | 6 ++-- .../mixin/entity/LivingEntityMixin.java | 6 ++-- .../mixin/entity/PlayerEntityMixin.java | 18 ++++++------ .../com/lambda/mixin/input/MouseMixin.java | 9 +++--- .../ClientPlayNetworkHandlerMixin.java | 2 +- .../AbstractSignBlockEntityRendererMixin.java | 4 ++- .../com/lambda/mixin/render/CameraMixin.java | 12 ++------ .../mixin/render/EntityRendererMixin.java | 6 ++-- .../mixin/render/HandledScreenMixin.java | 9 +++--- .../com/lambda/mixin/render/ScreenMixin.java | 5 ++-- .../com/lambda/event/events/MouseEvent.kt | 1 + .../com/lambda/interaction/BaritoneManager.kt | 29 +++++++++++++------ .../kotlin/com/lambda/module/hud/Baritone.kt | 9 ++++-- .../com/lambda/module/modules/player/Nuker.kt | 4 +-- src/main/resources/fabric.mod.json | 4 ++- src/main/resources/lambda.mixins.common.json | 11 ++++--- 18 files changed, 83 insertions(+), 61 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b62e11c39..1582e2065 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -176,7 +176,7 @@ dependencies { // Add mods /*modImplementation("com.github.rfresh2:baritone-fabric:$minecraftVersion") */ - modImplementation("com.github.rfresh2:baritone-fabric:1.21.10-SNAPSHOT") // ToDo: Move to 1.21.11 + modCompileOnly("com.github.rfresh2:baritone-fabric:1.21.10-SNAPSHOT") // ToDo: Move to 1.21.11 modCompileOnly("maven.modrinth:sodium:$sodiumVersion") modCompileOnly("maven.modrinth:malilib:$maLiLibVersion") modCompileOnly("maven.modrinth:litematica:$litematicaVersion") diff --git a/src/main/java/com/lambda/mixin/client/sound/SoundSystemMixin.java b/src/main/java/com/lambda/mixin/client/sound/SoundSystemMixin.java index abdbb841d..0df20004e 100644 --- a/src/main/java/com/lambda/mixin/client/sound/SoundSystemMixin.java +++ b/src/main/java/com/lambda/mixin/client/sound/SoundSystemMixin.java @@ -25,13 +25,14 @@ 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(SoundSystem.class) public class SoundSystemMixin { - @Inject(method = "play(Lnet/minecraft/client/sound/SoundInstance;)V", at = @At("HEAD"), cancellable = true) - public void onPlay(SoundInstance sound, CallbackInfo ci) { + @Inject(method = "play(Lnet/minecraft/client/sound/SoundInstance;)Lnet/minecraft/client/sound/SoundSystem$PlayResult;", at = @At("HEAD"), cancellable = true) + public void onPlay(SoundInstance sound, CallbackInfoReturnable cir) { if (EventFlow.post(new ClientEvent.Sound(sound)).isCanceled()) { - ci.cancel(); + cir.setReturnValue(SoundSystem.PlayResult.NOT_STARTED); } } } diff --git a/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java b/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java index 985115054..474f50574 100644 --- a/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java +++ b/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java @@ -121,9 +121,9 @@ private void cancelBlockBreakingPre(CallbackInfo ci) { if (EventFlow.post(new PlayerEvent.Breaking.Cancel(currentBreakingProgress)).isCanceled()) ci.cancel(); } - @WrapMethod(method = "createPlayer(Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/stat/StatHandler;Lnet/minecraft/client/recipebook/ClientRecipeBook;ZZ)Lnet/minecraft/client/network/ClientPlayerEntity;") - private ClientPlayerEntity injectCreatePlayer(ClientWorld world, StatHandler statHandler, ClientRecipeBook recipeBook, boolean lastSneaking, boolean lastSprinting, Operation original) { - var player = original.call(world, statHandler, recipeBook, lastSneaking, lastSprinting); + @WrapMethod(method = "createPlayer(Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/stat/StatHandler;Lnet/minecraft/client/recipebook/ClientRecipeBook;)Lnet/minecraft/client/network/ClientPlayerEntity;") + private ClientPlayerEntity wrapCreatePlayer(ClientWorld world, StatHandler statHandler, ClientRecipeBook recipeBook, Operation original) { + var player = original.call(world, statHandler, recipeBook); InventoryManager.INSTANCE.setScreenHandler(player.playerScreenHandler); return player; } diff --git a/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java b/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java index 74ececd16..e7178fa60 100644 --- a/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java +++ b/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java @@ -170,9 +170,9 @@ private float rotHead(LivingEntity entity, Operation original) { return (yaw == null) ? original.call(entity) : yaw; } - @ModifyConstant(method = "getHandSwingDuration", constant = @Constant(intValue = 6)) - private int getHandSwingDuration(int constant) { - if (lambda$instance != Lambda.getMc().player || ViewModel.INSTANCE.isDisabled()) return constant; + @WrapMethod(method = "getHandSwingDuration") + private int getHandSwingDuration(Operation original) { + if (lambda$instance != Lambda.getMc().player || ViewModel.INSTANCE.isDisabled()) return original.call(); return ViewModel.INSTANCE.getSwingDuration(); } diff --git a/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java b/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java index f6b7eeddc..15acd2497 100644 --- a/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java +++ b/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java @@ -47,13 +47,13 @@ private float wrapHeadYaw(PlayerEntity instance, Operation original) { return (yaw != null) ? yaw : original.call(instance); } - @WrapOperation(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;getYaw()F")) - private float wrapAttackYaw(PlayerEntity instance, Operation original) { - if ((Object) this != Lambda.getMc().player) { - return original.call(instance); - } - - Float yaw = RotationManager.getMovementYaw(); - return (yaw != null) ? yaw : original.call(instance); - } +// @WrapOperation(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;getYaw()F")) +// private float wrapAttackYaw(PlayerEntity instance, Operation original) { +// if ((Object) this != Lambda.getMc().player) { +// return original.call(instance); +// } +// +// Float yaw = RotationManager.getMovementYaw(); +// return (yaw != null) ? yaw : original.call(instance); +// } } diff --git a/src/main/java/com/lambda/mixin/input/MouseMixin.java b/src/main/java/com/lambda/mixin/input/MouseMixin.java index 6fb6b6f31..f025bc838 100644 --- a/src/main/java/com/lambda/mixin/input/MouseMixin.java +++ b/src/main/java/com/lambda/mixin/input/MouseMixin.java @@ -25,6 +25,7 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.client.Mouse; +import net.minecraft.client.input.MouseInput; import net.minecraft.client.option.SimpleOption; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -36,10 +37,10 @@ public class MouseMixin { @Shadow private double y; - @WrapMethod(method = "onMouseButton(JIII)V") - private void onMouseButton(long window, int button, int action, int mods, Operation original) { - if (!EventFlow.post(new MouseEvent.Click(button, action, mods)).isCanceled()) - original.call(window, button, action, mods); + @WrapMethod(method = "onMouseButton") + private void onMouseButton(long window, MouseInput input, int action, Operation original) { + if (!EventFlow.post(new MouseEvent.Click(input.button(), action, input.modifiers())).isCanceled()) + original.call(window, input, action); } @WrapMethod(method = "onMouseScroll(JDD)V") diff --git a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java index fb76f541a..99977ea81 100644 --- a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java @@ -53,7 +53,7 @@ void injectPlayerList(PlayerListS2CPacket.Action action, PlayerListS2CPacket.Ent } else EventFlow.post(new WorldEvent.Player.Leave(name, uuid, currentEntry)); } - @Inject(method = "onUpdateSelectedSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V", shift = At.Shift.AFTER), cancellable = true) + @Inject(method = "onUpdateSelectedSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/network/PacketApplyBatcher;)V", shift = At.Shift.AFTER), cancellable = true) private void onUpdateSelectedSlot(UpdateSelectedSlotS2CPacket packet, CallbackInfo ci) { if (EventFlow.post(new InventoryEvent.HotbarSlot.Sync(packet.slot())).isCanceled()) ci.cancel(); } diff --git a/src/main/java/com/lambda/mixin/render/AbstractSignBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/AbstractSignBlockEntityRendererMixin.java index 6f1d4c5c5..d43d82557 100644 --- a/src/main/java/com/lambda/mixin/render/AbstractSignBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/AbstractSignBlockEntityRendererMixin.java @@ -21,6 +21,8 @@ import net.minecraft.block.entity.SignText; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.block.entity.AbstractSignBlockEntityRenderer; +import net.minecraft.client.render.block.entity.state.SignBlockEntityRenderState; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.BlockPos; import org.spongepowered.asm.mixin.Mixin; @@ -31,7 +33,7 @@ @Mixin(AbstractSignBlockEntityRenderer.class) public class AbstractSignBlockEntityRendererMixin { @Inject(method = "renderText", at = @At("HEAD"), cancellable = true) - private void injectRenderText(BlockPos pos, SignText text, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int textLineHeight, int maxTextWidth, boolean front, CallbackInfo ci) { + private void injectRenderText(SignBlockEntityRenderState renderState, MatrixStack matrices, OrderedRenderCommandQueue queue, boolean front, CallbackInfo ci) { if (NoRender.INSTANCE.isEnabled() && NoRender.getNoSignText()) ci.cancel(); } } diff --git a/src/main/java/com/lambda/mixin/render/CameraMixin.java b/src/main/java/com/lambda/mixin/render/CameraMixin.java index 7b618d8be..aa77d7f12 100644 --- a/src/main/java/com/lambda/mixin/render/CameraMixin.java +++ b/src/main/java/com/lambda/mixin/render/CameraMixin.java @@ -26,6 +26,7 @@ import net.minecraft.client.render.Camera; import net.minecraft.entity.Entity; import net.minecraft.world.BlockView; +import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -42,14 +43,7 @@ public abstract class CameraMixin { public abstract void setRotation(float yaw, float pitch); @Inject(method = "update", at = @At("TAIL")) - private void onUpdate( - BlockView area, - Entity focusedEntity, - boolean thirdPerson, - boolean inverseView, - float tickDelta, - CallbackInfo ci - ) { + private void onUpdate(World area, Entity focusedEntity, boolean thirdPerson, boolean inverseView, float tickProgress, CallbackInfo ci) { if (!Freecam.INSTANCE.isEnabled()) return; Freecam.updateCam(); @@ -66,7 +60,7 @@ private void onUpdate( * } */ @Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;setPos(DDD)V", shift = At.Shift.AFTER)) - private void injectQuickPerspectiveSwap(BlockView area, Entity focusedEntity, boolean thirdPerson, boolean inverseView, float tickDelta, CallbackInfo ci) { + private void injectQuickPerspectiveSwap(World area, Entity focusedEntity, boolean thirdPerson, boolean inverseView, float tickProgress, CallbackInfo ci) { var rot = RotationManager.getLockRotation(); if (rot == null) return; setRotation(rot.getYawF(), rot.getPitchF()); diff --git a/src/main/java/com/lambda/mixin/render/EntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/EntityRendererMixin.java index 69af3188a..b71234dce 100644 --- a/src/main/java/com/lambda/mixin/render/EntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/EntityRendererMixin.java @@ -19,12 +19,12 @@ import com.lambda.module.modules.render.NoRender; import net.minecraft.client.render.Frustum; -import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.state.EntityRenderState; +import net.minecraft.client.render.state.CameraRenderState; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.Entity; -import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -39,7 +39,7 @@ private void injectShouldRender(Entity entity, Frustum frustum, double x, double } @Inject(method = "renderLabelIfPresent", at = @At("HEAD"), cancellable = true) - private void injectRenderLabelIfPresent(EntityRenderState state, Text text, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { + private void injectRenderLabelIfPresent(EntityRenderState state, MatrixStack matrices, OrderedRenderCommandQueue queue, CameraRenderState cameraRenderState, CallbackInfo ci) { if (NoRender.INSTANCE.isEnabled() && NoRender.getNoNametags()) ci.cancel(); } } diff --git a/src/main/java/com/lambda/mixin/render/HandledScreenMixin.java b/src/main/java/com/lambda/mixin/render/HandledScreenMixin.java index 193cdc516..3c66e9c53 100644 --- a/src/main/java/com/lambda/mixin/render/HandledScreenMixin.java +++ b/src/main/java/com/lambda/mixin/render/HandledScreenMixin.java @@ -18,6 +18,7 @@ package com.lambda.mixin.render; import com.lambda.module.modules.render.ContainerPreview; +import net.minecraft.client.gui.Click; import net.minecraft.client.gui.screen.ingame.HandledScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -27,18 +28,18 @@ @Mixin(HandledScreen.class) public class HandledScreenMixin { @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) - private void onMouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { + private void onMouseClicked(Click click, boolean doubled, CallbackInfoReturnable cir) { if (ContainerPreview.INSTANCE.isEnabled() && ContainerPreview.isLocked()) { - if (ContainerPreview.isMouseOverLockedTooltip((int) mouseX, (int) mouseY)) { + if (ContainerPreview.isMouseOverLockedTooltip((int) click.x(), (int) click.y())) { cir.setReturnValue(true); } } } @Inject(method = "mouseReleased", at = @At("HEAD"), cancellable = true) - private void onMouseReleased(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { + private void onMouseReleased(Click click, CallbackInfoReturnable cir) { if (ContainerPreview.INSTANCE.isEnabled() && ContainerPreview.isLocked()) { - if (ContainerPreview.isMouseOverLockedTooltip((int) mouseX, (int) mouseY)) { + if (ContainerPreview.isMouseOverLockedTooltip((int) click.x(), (int) click.y())) { cir.setReturnValue(true); } } diff --git a/src/main/java/com/lambda/mixin/render/ScreenMixin.java b/src/main/java/com/lambda/mixin/render/ScreenMixin.java index 9c4972653..6ed4f2c53 100644 --- a/src/main/java/com/lambda/mixin/render/ScreenMixin.java +++ b/src/main/java/com/lambda/mixin/render/ScreenMixin.java @@ -25,6 +25,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.input.KeyInput; import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -35,8 +36,8 @@ @Mixin(Screen.class) public class ScreenMixin { @Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true) - private void onKeyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE && QuickSearch.INSTANCE.isOpen()) { + private void onKeyPressed(KeyInput input, CallbackInfoReturnable cir) { + if (input.key() == GLFW.GLFW_KEY_ESCAPE && QuickSearch.INSTANCE.isOpen()) { QuickSearch.INSTANCE.close(); cir.setReturnValue(true); } diff --git a/src/main/kotlin/com/lambda/event/events/MouseEvent.kt b/src/main/kotlin/com/lambda/event/events/MouseEvent.kt index 427969e39..3fcb9d3f7 100644 --- a/src/main/kotlin/com/lambda/event/events/MouseEvent.kt +++ b/src/main/kotlin/com/lambda/event/events/MouseEvent.kt @@ -21,6 +21,7 @@ import com.lambda.config.settings.complex.Bind import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable import com.lambda.util.math.Vec2d +import net.minecraft.client.input.MouseInput sealed class MouseEvent { /** diff --git a/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt b/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt index 43f36c52c..97a448780 100644 --- a/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt +++ b/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt @@ -28,15 +28,18 @@ import com.lambda.context.Automated import com.lambda.config.AutomationConfig import com.lambda.util.BlockUtils.blockPos import com.lambda.util.NamedEnum +import net.fabricmc.loader.api.FabricLoader object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConfig.Companion.DEFAULT { override val name = "baritone" - private val baritone = BaritoneAPI.getProvider() - val baritoneSettings: Settings = BaritoneAPI.getSettings() + val isBaritoneLoaded = FabricLoader.getInstance().isModLoaded("baritone") + + private val baritone = if (isBaritoneLoaded) BaritoneAPI.getProvider() else null + val baritoneSettings: Settings? = if (isBaritoneLoaded) BaritoneAPI.getSettings() else null @JvmStatic - val primary: IBaritone = baritone.primaryBaritone + val primary: IBaritone? = baritone?.primaryBaritone private enum class Group(override val displayName: String) : NamedEnum { General("General"), @@ -72,7 +75,8 @@ object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConf init { // ToDo: Dont actually save the settings as its duplicate data - with(baritoneSettings) { + if (isBaritoneLoaded) { + with(baritoneSettings!!) { // GENERAL setting("Log As Toast", logAsToast.value).group(Group.General, SubGroup.ChatAndControl).onValueChange { _, it -> logAsToast.value = it } @@ -334,6 +338,7 @@ object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConf setting("Allow Land On Nether Fortress", elytraAllowLandOnNetherFortress.value).group(Group.Elytra).onValueChange { _, it -> elytraAllowLandOnNetherFortress.value = it } setting("Terms Accepted", elytraTermsAccepted.value).group(Group.Elytra).onValueChange { _, it -> elytraTermsAccepted.value = it } setting("Chat Spam", elytraChatSpam.value).group(Group.Elytra).onValueChange { _, it -> elytraChatSpam.value = it } + } } } @@ -341,22 +346,28 @@ object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConf * Whether Baritone is currently pathing */ val isPathing: Boolean - get() = primary.pathingBehavior.isPathing + get() = isBaritoneLoaded && primary?.pathingBehavior?.isPathing == true /** * Whether Baritone is active (pathing, calculating goal, etc.) */ val isActive: Boolean - get() = primary.customGoalProcess.isActive || primary.pathingBehavior.isPathing || primary.pathingControlManager.mostRecentInControl() - .orElse(null)?.isActive == true + get() = isBaritoneLoaded && (primary?.customGoalProcess?.isActive == true || primary?.pathingBehavior?.isPathing == true || primary?.pathingControlManager?.mostRecentInControl() + ?.orElse(null)?.isActive == true) /** * Sets the current Baritone goal and starts pathing */ - fun setGoalAndPath(goal: Goal) = primary.customGoalProcess.setGoalAndPath(goal) + fun setGoalAndPath(goal: Goal) { + if (!isBaritoneLoaded) return + primary?.customGoalProcess?.setGoalAndPath(goal) + } /** * Force cancel Baritone */ - fun cancel() = primary.pathingBehavior.cancelEverything() + fun cancel() { + if (!isBaritoneLoaded) return + primary?.pathingBehavior?.cancelEverything() + } } diff --git a/src/main/kotlin/com/lambda/module/hud/Baritone.kt b/src/main/kotlin/com/lambda/module/hud/Baritone.kt index 8d20b3f62..86bae308f 100644 --- a/src/main/kotlin/com/lambda/module/hud/Baritone.kt +++ b/src/main/kotlin/com/lambda/module/hud/Baritone.kt @@ -18,7 +18,7 @@ package com.lambda.module.hud import com.lambda.gui.dsl.ImGuiBuilder -import com.lambda.interaction.BaritoneManager.primary +import com.lambda.interaction.BaritoneManager import com.lambda.interaction.construction.simulation.BuildGoal import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag @@ -29,7 +29,12 @@ object Baritone : HudModule( tag = ModuleTag.HUD, ) { override fun ImGuiBuilder.buildLayout() { - primary.customGoalProcess.goal?.let { + if (!BaritoneManager.isBaritoneLoaded) { + text("Baritone is not loaded") + return + } + + BaritoneManager.primary?.customGoalProcess?.goal?.let { when(it) { is BuildGoal -> text("Lambda Simulation: ${it.sim}") else -> text("Baritone: $it") diff --git a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 4591c084a..25ba05ffb 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -64,13 +64,13 @@ object Nuker : Module( .filter { !flatten || it.y >= player.blockPos.y } .filter { pos -> if (!baritoneSelection) true - else BaritoneManager.primary.selectionManager.selections.any { + else BaritoneManager.primary?.selectionManager?.selections?.any { val min = it.min() val max = it.max() pos.x >= min.x && pos.x <= max.x && pos.y >= min.y && pos.y <= max.y && pos.z >= min.z && pos.z <= max.z - } + } ?: false } .associateWith { if (fillFluids) TargetState.Air else TargetState.Empty } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 50e5c4edf..7110a6c21 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -29,7 +29,9 @@ "minecraft": "~$minecraftVersion", "fabric-api": ">=$fabricApiVersion+$minecraftVersion", "fabric-language-kotlin": ">=$kotlinFabricVersion", - "java": ">=$javaVersion", + "java": ">=$javaVersion" + }, + "recommends": { "baritone": "*" } } diff --git a/src/main/resources/lambda.mixins.common.json b/src/main/resources/lambda.mixins.common.json index 1334a327f..6eeb82640 100644 --- a/src/main/resources/lambda.mixins.common.json +++ b/src/main/resources/lambda.mixins.common.json @@ -6,8 +6,6 @@ "client": [ "CrashReportMixin", "MinecraftClientMixin", - "baritone.MixinBaritonePlayerContext", - "baritone.MixinLookBehavior", "client.sound.SoundSystemMixin", "entity.ClientPlayerEntityMixin", "entity.ClientPlayInteractionManagerMixin", @@ -83,9 +81,14 @@ "world.ClientChunkManagerMixin", "world.ClientWorldMixin", "world.StructureTemplateMixin", - "world.WorldMixin" + "world.WorldMixin", + "baritone.MixinBaritonePlayerContext", + "baritone.MixinLookBehavior" ], "injectors": { - "defaultRequire": 1 + "defaultRequire": 0 + }, + "overwrites": { + "conformVisibility": true } } From 8b426d934b28a8edbd6dffe1b3923b21b4b3dbbc Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 17 Dec 2025 18:06:48 +0100 Subject: [PATCH 05/20] Even more mixins --- .../lambda/mixin/MinecraftClientMixin.java | 27 ++-- .../render/ArmorFeatureRendererMixin.java | 6 +- .../mixin/render/BackgroundRendererMixin.java | 9 +- .../mixin/render/ChatInputSuggestorMixin.java | 2 +- .../mixin/render/DarknessEffectFogMixin.java | 9 +- .../lambda/mixin/render/DebugHudMixin.java | 8 +- .../render/HeadFeatureRendererMixin.java | 6 +- .../mixin/render/HeldItemRendererMixin.java | 118 +++++++++--------- .../render/InGameOverlayRendererMixin.java | 4 +- .../render/LightmapTextureManagerMixin.java | 2 +- .../mixin/render/ParticleManagerMixin.java | 18 +-- .../render/WorldBorderRenderingMixin.java | 3 +- .../mixin/render/WorldRendererMixin.java | 32 ++--- .../AbstractSignBlockEntityRendererMixin.java | 5 +- .../BeaconBlockEntityRendererMixin.java | 12 +- .../BlockEntityRenderDispatcherMixin.java | 2 +- ...chantingTableBlockEntityRendererMixin.java | 18 +-- .../MobSpawnerBlockEntityRendererMixin.java | 10 +- .../lambda/mixin/world/ClientWorldMixin.java | 30 ++--- src/main/resources/lambda.mixins.common.json | 10 +- 20 files changed, 171 insertions(+), 160 deletions(-) rename src/main/java/com/lambda/mixin/render/{ => blockEntity}/AbstractSignBlockEntityRendererMixin.java (90%) rename src/main/java/com/lambda/mixin/render/{ => blockEntity}/BeaconBlockEntityRendererMixin.java (61%) rename src/main/java/com/lambda/mixin/render/{ => blockEntity}/BlockEntityRenderDispatcherMixin.java (97%) rename src/main/java/com/lambda/mixin/render/{ => blockEntity}/EnchantingTableBlockEntityRendererMixin.java (50%) rename src/main/java/com/lambda/mixin/render/{ => blockEntity}/MobSpawnerBlockEntityRendererMixin.java (69%) diff --git a/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 83ec7b6ef..c557264ac 100644 --- a/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -46,6 +46,7 @@ import net.minecraft.util.hit.HitResult; import net.minecraft.util.thread.ThreadExecutor; import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -106,18 +107,18 @@ void onInput(MinecraftClient instance, Operation original) { this.lambda$inputHandledThisTick = true; } - @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;tick()V")) - void onWorldRenderer(WorldRenderer instance, Operation original) { - if (!this.lambda$inputHandledThisTick) { - EventFlow.post(TickEvent.Input.Pre.INSTANCE); - EventFlow.post(TickEvent.Input.Post.INSTANCE); - this.lambda$inputHandledThisTick = true; - } - - EventFlow.post(TickEvent.WorldRender.Pre.INSTANCE); - original.call(instance); - EventFlow.post(TickEvent.WorldRender.Post.INSTANCE); - } +// @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;tick()V")) +// void onWorldRenderer(WorldRenderer instance, Operation original) { +// if (!this.lambda$inputHandledThisTick) { +// EventFlow.post(TickEvent.Input.Pre.INSTANCE); +// EventFlow.post(TickEvent.Input.Post.INSTANCE); +// this.lambda$inputHandledThisTick = true; +// } +// +// EventFlow.post(TickEvent.WorldRender.Pre.INSTANCE); +// original.call(instance); +// EventFlow.post(TickEvent.WorldRender.Post.INSTANCE); +// } @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/sound/SoundManager;tick(Z)V")) void onSound(SoundManager instance, boolean paused, Operation original) { @@ -134,7 +135,7 @@ private void onShutdown(CallbackInfo ci) { /** * Inject after the thread field is set so that {@link ThreadExecutor#getThread} is available */ - @Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/MinecraftClient;thread:Ljava/lang/Thread;", shift = At.Shift.AFTER, ordinal = 0), method = "run") + @Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/MinecraftClient;thread:Ljava/lang/Thread;", shift = At.Shift.AFTER, ordinal = 0, opcode = Opcodes.PUTFIELD), method = "run") private void onStartup(CallbackInfo ci) { EventFlow.post(new ClientEvent.Startup()); } diff --git a/src/main/java/com/lambda/mixin/render/ArmorFeatureRendererMixin.java b/src/main/java/com/lambda/mixin/render/ArmorFeatureRendererMixin.java index cc5ee9e77..77ca02496 100644 --- a/src/main/java/com/lambda/mixin/render/ArmorFeatureRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/ArmorFeatureRendererMixin.java @@ -19,8 +19,10 @@ import com.lambda.module.modules.render.NoRender; import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; import net.minecraft.client.render.entity.state.BipedEntityRenderState; +import net.minecraft.client.render.entity.state.EntityRenderState; import net.minecraft.client.util.math.MatrixStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -29,8 +31,8 @@ @Mixin(ArmorFeatureRenderer.class) public class ArmorFeatureRendererMixin { - @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/BipedEntityRenderState;FF)V", at = @At("HEAD"), cancellable = true) - private void injectRender(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, BipedEntityRenderState bipedEntityRenderState, float f, float g, CallbackInfo ci) { + @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;ILnet/minecraft/client/render/entity/state/EntityRenderState;FF)V", at = @At("HEAD"), cancellable = true) + private void injectRender(MatrixStack matrices, OrderedRenderCommandQueue queue, int light, EntityRenderState state, float limbAngle, float limbDistance, CallbackInfo ci) { if (NoRender.INSTANCE.isEnabled() && NoRender.getNoArmor()) ci.cancel(); } } diff --git a/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java b/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java index 9fe12e8af..887ca3b6b 100644 --- a/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/BackgroundRendererMixin.java @@ -22,6 +22,7 @@ import net.minecraft.client.render.fog.DarknessEffectFogModifier; import net.minecraft.block.enums.CameraSubmersionType; import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -36,10 +37,10 @@ @Mixin(BlindnessEffectFogModifier.class) public class BackgroundRendererMixin { - @Inject(method = "shouldApply", at = @At("HEAD"), cancellable = true) - private void injectShouldApplyBlindness(CameraSubmersionType submersionType, Entity cameraEntity, CallbackInfoReturnable cir) { - if (NoRender.INSTANCE.getNoBlindness() && NoRender.INSTANCE.isEnabled()) { - cir.setReturnValue(false); + @Inject(method = "applyDarknessModifier", at = @At("HEAD"), cancellable = true) + private void injectShouldApplyBlindness(LivingEntity cameraEntity, float darkness, float tickProgress, CallbackInfoReturnable cir) { + if (NoRender.getNoBlindness() && NoRender.INSTANCE.isEnabled()) { + cir.setReturnValue(0.0f); } } } diff --git a/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java b/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java index 01966adc2..2e0d8f54a 100644 --- a/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java +++ b/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java @@ -65,7 +65,7 @@ private boolean refreshModify(boolean showCompletions) { @SuppressWarnings("unchecked") @WrapOperation(method = "refresh", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;getCommandDispatcher()Lcom/mojang/brigadier/CommandDispatcher;")) private CommandDispatcher wrapRefresh(ClientPlayNetworkHandler instance, Operation> original) { - return (CommandDispatcher) (Object) CommandManager.INSTANCE.currentDispatcher(textField.getText()); + return (CommandDispatcher) CommandManager.INSTANCE.currentDispatcher(textField.getText()); } @Inject(method = "refresh", at = @At("TAIL")) diff --git a/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java b/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java index 72e10a19d..af0c941e7 100644 --- a/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java +++ b/src/main/java/com/lambda/mixin/render/DarknessEffectFogMixin.java @@ -21,6 +21,7 @@ import net.minecraft.client.render.fog.DarknessEffectFogModifier; import net.minecraft.block.enums.CameraSubmersionType; import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -32,10 +33,10 @@ @Mixin(DarknessEffectFogModifier.class) public class DarknessEffectFogMixin { - @Inject(method = "shouldApply", at = @At("HEAD"), cancellable = true) - private void injectShouldApplyDarkness(CameraSubmersionType submersionType, Entity cameraEntity, CallbackInfoReturnable cir) { - if (NoRender.INSTANCE.getNoDarkness() && NoRender.INSTANCE.isEnabled()) { - cir.setReturnValue(false); + @Inject(method = "applyDarknessModifier", at = @At("HEAD"), cancellable = true) + private void injectShouldApplyDarkness(LivingEntity cameraEntity, float darkness, float tickProgress, CallbackInfoReturnable cir) { + if (NoRender.getNoDarkness() && NoRender.INSTANCE.isEnabled()) { + cir.setReturnValue(0.0f); } } } diff --git a/src/main/java/com/lambda/mixin/render/DebugHudMixin.java b/src/main/java/com/lambda/mixin/render/DebugHudMixin.java index 4b29a9e66..28c47db4b 100644 --- a/src/main/java/com/lambda/mixin/render/DebugHudMixin.java +++ b/src/main/java/com/lambda/mixin/render/DebugHudMixin.java @@ -28,8 +28,8 @@ @Mixin(DebugHud.class) public class DebugHudMixin { - @Inject(method = "getRightText", at = @At("TAIL")) - private void onGetRightText(CallbackInfoReturnable> cir) { - DebugInfoHud.addDebugInfo(cir.getReturnValue()); - } +// @Inject(method = "getRightText", at = @At("TAIL")) +// private void onGetRightText(CallbackInfoReturnable> cir) { +// DebugInfoHud.addDebugInfo(cir.getReturnValue()); +// } } diff --git a/src/main/java/com/lambda/mixin/render/HeadFeatureRendererMixin.java b/src/main/java/com/lambda/mixin/render/HeadFeatureRendererMixin.java index 38834b1ac..bb8416614 100644 --- a/src/main/java/com/lambda/mixin/render/HeadFeatureRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/HeadFeatureRendererMixin.java @@ -19,7 +19,9 @@ import com.lambda.module.modules.render.NoRender; import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.feature.HeadFeatureRenderer; +import net.minecraft.client.render.entity.state.EntityRenderState; import net.minecraft.client.render.entity.state.LivingEntityRenderState; import net.minecraft.client.util.math.MatrixStack; import org.spongepowered.asm.mixin.Mixin; @@ -29,8 +31,8 @@ @Mixin(HeadFeatureRenderer.class) public class HeadFeatureRendererMixin { - @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V", at = @At("HEAD"), cancellable = true) - private void injectRender(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, LivingEntityRenderState livingEntityRenderState, float f, float g, CallbackInfo ci) { + @Inject(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;ILnet/minecraft/client/render/entity/state/EntityRenderState;FF)V", at = @At("HEAD"), cancellable = true) + private void injectRender(MatrixStack matrices, OrderedRenderCommandQueue queue, int light, EntityRenderState state, float limbAngle, float limbDistance, CallbackInfo ci) { if (NoRender.INSTANCE.isEnabled() && NoRender.getNoArmor() && NoRender.getIncludeNoOtherHeadItems()) ci.cancel(); } } diff --git a/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java b/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java index 48d965c1a..6aacbb9f2 100644 --- a/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java @@ -43,63 +43,63 @@ public class HeldItemRendererMixin { @Shadow private ItemStack offHand; @Shadow private float equipProgressMainHand; @Shadow private float equipProgressOffHand; - - @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderArmHoldingItem(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IFFLnet/minecraft/util/Arm;)V")) - private void onRenderArmHoldingItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack itemStack, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { - if (!ViewModel.INSTANCE.isEnabled()) return; - - ViewModel.INSTANCE.transform(itemStack, hand, matrices); - } - - @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemDisplayContext;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", ordinal = 1)) - private void onRenderFirstPersonItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack itemStack, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { - if (!ViewModel.INSTANCE.isEnabled()) return; - - ViewModel.INSTANCE.transform(itemStack, hand, matrices); - } - - @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 2), index = 0) - private float modifyEquipProgressMainHand(float value) { - if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; - - ViewModel config = ViewModel.INSTANCE; - ItemStack currentStack = client.player.getMainHandStack(); - if (config.getOldAnimations() && !config.getSwapAnimation()) { - mainHand = currentStack; - } - - float progress = config.getOldAnimations() ? 1 : (float) Math.pow(client.player.getAttackCooldownProgress(1), 3); - - return (ItemStack.areEqual(mainHand, currentStack) ? progress : 0) - equipProgressMainHand; - } - - @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 3), index = 0) - private float modifyEquipProgressOffHand(float value) { - if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; - - ViewModel config = ViewModel.INSTANCE; - - ItemStack currentStack = client.player.getOffHandStack(); - if (config.getOldAnimations() && !config.getSwapAnimation()) { - offHand = currentStack; - } - - return (ItemStack.areEqual(offHand, currentStack) ? 1 : 0) - equipProgressOffHand; - } - - @ModifyVariable(method = "renderItem(FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/network/ClientPlayerEntity;I)V", at = @At(value = "STORE", ordinal = 0), index = 6) - private float modifySwing(float swingProgress) { - ViewModel config = ViewModel.INSTANCE; - MinecraftClient mc = Lambda.getMc(); - if (config.isDisabled() || mc.player == null) return swingProgress; - Hand hand = MoreObjects.firstNonNull(mc.player.preferredHand, Hand.MAIN_HAND); - - if (hand == Hand.MAIN_HAND) { - return swingProgress + config.getMainSwingProgress(); - } else if (hand == Hand.OFF_HAND) { - return swingProgress + config.getOffhandSwingProgress(); - } - - return swingProgress; - } +// +// @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderArmHoldingItem(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IFFLnet/minecraft/util/Arm;)V")) +// private void onRenderArmHoldingItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack itemStack, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { +// if (!ViewModel.INSTANCE.isEnabled()) return; +// +// ViewModel.INSTANCE.transform(itemStack, hand, matrices); +// } +// +// @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemDisplayContext;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", ordinal = 1)) +// private void onRenderFirstPersonItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack itemStack, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { +// if (!ViewModel.INSTANCE.isEnabled()) return; +// +// ViewModel.INSTANCE.transform(itemStack, hand, matrices); +// } +// +// @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 2), index = 0) +// private float modifyEquipProgressMainHand(float value) { +// if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; +// +// ViewModel config = ViewModel.INSTANCE; +// ItemStack currentStack = client.player.getMainHandStack(); +// if (config.getOldAnimations() && !config.getSwapAnimation()) { +// mainHand = currentStack; +// } +// +// float progress = config.getOldAnimations() ? 1 : (float) Math.pow(client.player.getAttackCooldownProgress(1), 3); +// +// return (ItemStack.areEqual(mainHand, currentStack) ? progress : 0) - equipProgressMainHand; +// } +// +// @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 3), index = 0) +// private float modifyEquipProgressOffHand(float value) { +// if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; +// +// ViewModel config = ViewModel.INSTANCE; +// +// ItemStack currentStack = client.player.getOffHandStack(); +// if (config.getOldAnimations() && !config.getSwapAnimation()) { +// offHand = currentStack; +// } +// +// return (ItemStack.areEqual(offHand, currentStack) ? 1 : 0) - equipProgressOffHand; +// } +// +// @ModifyVariable(method = "renderItem(FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/network/ClientPlayerEntity;I)V", at = @At(value = "STORE", ordinal = 0), index = 6) +// private float modifySwing(float swingProgress) { +// ViewModel config = ViewModel.INSTANCE; +// MinecraftClient mc = Lambda.getMc(); +// if (config.isDisabled() || mc.player == null) return swingProgress; +// Hand hand = MoreObjects.firstNonNull(mc.player.preferredHand, Hand.MAIN_HAND); +// +// if (hand == Hand.MAIN_HAND) { +// return swingProgress + config.getMainSwingProgress(); +// } else if (hand == Hand.OFF_HAND) { +// return swingProgress + config.getOffhandSwingProgress(); +// } +// +// return swingProgress; +// } } diff --git a/src/main/java/com/lambda/mixin/render/InGameOverlayRendererMixin.java b/src/main/java/com/lambda/mixin/render/InGameOverlayRendererMixin.java index 49412089b..956abd82e 100644 --- a/src/main/java/com/lambda/mixin/render/InGameOverlayRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/InGameOverlayRendererMixin.java @@ -32,9 +32,9 @@ @Mixin(InGameOverlayRenderer.class) public class InGameOverlayRendererMixin { @WrapMethod(method = "renderFireOverlay") - private static void wrapRenderFireOverlay(MatrixStack matrices, VertexConsumerProvider vertexConsumers, Operation original) { + private static void wrapRenderFireOverlay(MatrixStack matrices, VertexConsumerProvider vertexConsumers, Sprite sprite, Operation original) { if (!(NoRender.INSTANCE.isEnabled() && NoRender.getNoFireOverlay())) { - original.call(matrices, vertexConsumers); + original.call(matrices, vertexConsumers, sprite); } } diff --git a/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java b/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java index e8cddbbd2..48dec03ed 100644 --- a/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java +++ b/src/main/java/com/lambda/mixin/render/LightmapTextureManagerMixin.java @@ -54,7 +54,7 @@ private void injectUpdate(float tickProgress, CallbackInfo ci) { @ModifyReturnValue(method = "getDarkness", at = @At("RETURN")) private float modifyGetDarkness(float original, LivingEntity entity, float factor, float tickProgress) { - if (NoRender.INSTANCE.getNoDarkness() && NoRender.INSTANCE.isEnabled()) return 0.0f; + if (NoRender.getNoDarkness() && NoRender.INSTANCE.isEnabled()) return 0.0f; return original; } } diff --git a/src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java b/src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java index 654e32316..f5e56cd3f 100644 --- a/src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java +++ b/src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java @@ -41,15 +41,15 @@ public class ParticleManagerMixin { // if (NoRender.shouldOmitParticle(particle)) ci.cancel(); // } - @WrapOperation(method = "renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleManager;renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/particle/ParticleTextureSheet;Ljava/util/Queue;)V")) - private void wrapRenderParticles(Camera camera, float tickProgress, VertexConsumerProvider.Immediate vertexConsumers, ParticleTextureSheet sheet, Queue particles, Operation original) { - original.call(camera, tickProgress, vertexConsumers, sheet, filterParticles(particles)); - } - - @WrapOperation(method = "renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleManager;renderCustomParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;Ljava/util/Queue;)V")) - private void wrapRenderParticles(Camera camera, float tickProgress, VertexConsumerProvider.Immediate vertexConsumers, Queue particles, Operation original) { - original.call(camera, tickProgress, vertexConsumers, filterParticles(particles)); - } +// @WrapOperation(method = "renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleManager;renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/particle/ParticleTextureSheet;Ljava/util/Queue;)V")) +// private void wrapRenderParticles(Camera camera, float tickProgress, VertexConsumerProvider.Immediate vertexConsumers, ParticleTextureSheet sheet, Queue particles, Operation original) { +// original.call(camera, tickProgress, vertexConsumers, sheet, filterParticles(particles)); +// } +// +// @WrapOperation(method = "renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleManager;renderCustomParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;Ljava/util/Queue;)V")) +// private void wrapRenderParticles(Camera camera, float tickProgress, VertexConsumerProvider.Immediate vertexConsumers, Queue particles, Operation original) { +// original.call(camera, tickProgress, vertexConsumers, filterParticles(particles)); +// } @Unique private Queue filterParticles(Queue particles) { diff --git a/src/main/java/com/lambda/mixin/render/WorldBorderRenderingMixin.java b/src/main/java/com/lambda/mixin/render/WorldBorderRenderingMixin.java index a5e6f9269..9553a39a5 100644 --- a/src/main/java/com/lambda/mixin/render/WorldBorderRenderingMixin.java +++ b/src/main/java/com/lambda/mixin/render/WorldBorderRenderingMixin.java @@ -19,6 +19,7 @@ import com.lambda.module.modules.render.NoRender; import net.minecraft.client.render.WorldBorderRendering; +import net.minecraft.client.render.state.WorldBorderRenderState; import net.minecraft.util.math.Vec3d; import net.minecraft.world.border.WorldBorder; import org.spongepowered.asm.mixin.Mixin; @@ -29,7 +30,7 @@ @Mixin(WorldBorderRendering.class) public class WorldBorderRenderingMixin { @Inject(method = "render", at = @At("HEAD"), cancellable = true) - private void injectRender(WorldBorder border, Vec3d cameraPos, double viewDistanceBlocks, double farPlaneDistance, CallbackInfo ci) { + private void injectRender(WorldBorderRenderState state, Vec3d cameraPos, double viewDistanceBlocks, double farPlaneDistance, CallbackInfo ci) { if (NoRender.INSTANCE.isEnabled() && NoRender.getNoWorldBorder()) ci.cancel(); } } diff --git a/src/main/java/com/lambda/mixin/render/WorldRendererMixin.java b/src/main/java/com/lambda/mixin/render/WorldRendererMixin.java index d8eb33e08..dcac99936 100644 --- a/src/main/java/com/lambda/mixin/render/WorldRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/WorldRendererMixin.java @@ -34,22 +34,22 @@ @Mixin(WorldRenderer.class) public class WorldRendererMixin { - @ModifyExpressionValue(method = "getEntitiesToRender(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;Ljava/util/List;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;isThirdPerson()Z")) - private boolean renderIsThirdPerson(boolean original) { - return Freecam.INSTANCE.isEnabled() || original; - } - - @ModifyArg(method = "render(Lnet/minecraft/client/util/ObjectAllocator;Lnet/minecraft/client/render/RenderTickCounter;ZLnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/GameRenderer;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V"), index = 3) - private boolean renderSetupTerrainModifyArg(boolean hasForcedFrustum) { - return Freecam.INSTANCE.isEnabled() || CameraTweaks.INSTANCE.isEnabled() || hasForcedFrustum; - } - - @ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/BackgroundRenderer;applyFog(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/BackgroundRenderer$FogType;Lorg/joml/Vector4f;FZF)Lnet/minecraft/client/render/Fog;", ordinal = 0), index = 3) - private float modifyApplyFogRenderDistance(float viewDistance) { - return NoRender.INSTANCE.isEnabled() && NoRender.getNoTerrainFog() - ? Float.MAX_VALUE - : viewDistance; - } +// @ModifyExpressionValue(method = "getEntitiesToRender(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;Ljava/util/List;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;isThirdPerson()Z")) +// private boolean renderIsThirdPerson(boolean original) { +// return Freecam.INSTANCE.isEnabled() || original; +// } +// +// @ModifyArg(method = "render(Lnet/minecraft/client/util/ObjectAllocator;Lnet/minecraft/client/render/RenderTickCounter;ZLnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/GameRenderer;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V"), index = 3) +// private boolean renderSetupTerrainModifyArg(boolean hasForcedFrustum) { +// return Freecam.INSTANCE.isEnabled() || CameraTweaks.INSTANCE.isEnabled() || hasForcedFrustum; +// } +// +// @ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/BackgroundRenderer;applyFog(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/BackgroundRenderer$FogType;Lorg/joml/Vector4f;FZF)Lnet/minecraft/client/render/Fog;", ordinal = 0), index = 3) +// private float modifyApplyFogRenderDistance(float viewDistance) { +// return NoRender.INSTANCE.isEnabled() && NoRender.getNoTerrainFog() +// ? Float.MAX_VALUE +// : viewDistance; +// } @Inject(method = "hasBlindnessOrDarkness(Lnet/minecraft/client/render/Camera;)Z", at = @At(value = "HEAD"), cancellable = true) private void modifyEffectCheck(Camera camera, CallbackInfoReturnable cir) { diff --git a/src/main/java/com/lambda/mixin/render/AbstractSignBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockEntity/AbstractSignBlockEntityRendererMixin.java similarity index 90% rename from src/main/java/com/lambda/mixin/render/AbstractSignBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockEntity/AbstractSignBlockEntityRendererMixin.java index d43d82557..bd3f93ac6 100644 --- a/src/main/java/com/lambda/mixin/render/AbstractSignBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockEntity/AbstractSignBlockEntityRendererMixin.java @@ -15,16 +15,13 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render; +package com.lambda.mixin.render.blockEntity; import com.lambda.module.modules.render.NoRender; -import net.minecraft.block.entity.SignText; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.block.entity.AbstractSignBlockEntityRenderer; import net.minecraft.client.render.block.entity.state.SignBlockEntityRenderState; import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.math.BlockPos; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/java/com/lambda/mixin/render/BeaconBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockEntity/BeaconBlockEntityRendererMixin.java similarity index 61% rename from src/main/java/com/lambda/mixin/render/BeaconBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockEntity/BeaconBlockEntityRendererMixin.java index a3d4b5e38..d86cf5493 100644 --- a/src/main/java/com/lambda/mixin/render/BeaconBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockEntity/BeaconBlockEntityRendererMixin.java @@ -15,14 +15,14 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render; +package com.lambda.mixin.render.blockEntity; import com.lambda.module.modules.render.NoRender; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer; +import net.minecraft.client.render.block.entity.state.BeaconBlockEntityRenderState; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; +import net.minecraft.client.render.state.CameraRenderState; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -30,8 +30,8 @@ @Mixin(BeaconBlockEntityRenderer.class) public class BeaconBlockEntityRendererMixin { - @Inject(method = "render", at = @At("HEAD"), cancellable = true) - private void injectRender(BlockEntity entity, float tickProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, Vec3d cameraPos, CallbackInfo ci) { + @Inject(method = "render(Lnet/minecraft/client/render/block/entity/state/BeaconBlockEntityRenderState;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;Lnet/minecraft/client/render/state/CameraRenderState;)V", at = @At("HEAD"), cancellable = true) + private void injectRender(BeaconBlockEntityRenderState beaconBlockEntityRenderState, MatrixStack matrixStack, OrderedRenderCommandQueue orderedRenderCommandQueue, CameraRenderState cameraRenderState, CallbackInfo ci) { if (NoRender.INSTANCE.isEnabled() && NoRender.getNoBeaconBeams()) ci.cancel(); } } diff --git a/src/main/java/com/lambda/mixin/render/BlockEntityRenderDispatcherMixin.java b/src/main/java/com/lambda/mixin/render/blockEntity/BlockEntityRenderDispatcherMixin.java similarity index 97% rename from src/main/java/com/lambda/mixin/render/BlockEntityRenderDispatcherMixin.java rename to src/main/java/com/lambda/mixin/render/blockEntity/BlockEntityRenderDispatcherMixin.java index 06e6a92b5..6d9a5523a 100644 --- a/src/main/java/com/lambda/mixin/render/BlockEntityRenderDispatcherMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockEntity/BlockEntityRenderDispatcherMixin.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render; +package com.lambda.mixin.render.blockEntity; import com.lambda.module.modules.render.NoRender; import net.minecraft.block.entity.BlockEntity; diff --git a/src/main/java/com/lambda/mixin/render/EnchantingTableBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockEntity/EnchantingTableBlockEntityRendererMixin.java similarity index 50% rename from src/main/java/com/lambda/mixin/render/EnchantingTableBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockEntity/EnchantingTableBlockEntityRendererMixin.java index f80ca373d..8046aac2f 100644 --- a/src/main/java/com/lambda/mixin/render/EnchantingTableBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockEntity/EnchantingTableBlockEntityRendererMixin.java @@ -15,21 +15,23 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render; +package com.lambda.mixin.render.blockEntity; import com.lambda.module.modules.render.NoRender; -import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; -import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.block.entity.EnchantingTableBlockEntityRenderer; -import net.minecraft.client.render.entity.model.BookModel; +import net.minecraft.client.render.block.entity.state.EnchantingTableBlockEntityRenderState; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; +import net.minecraft.client.render.state.CameraRenderState; import net.minecraft.client.util.math.MatrixStack; 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(EnchantingTableBlockEntityRenderer.class) public class EnchantingTableBlockEntityRendererMixin { - @WrapWithCondition(method = "render(Lnet/minecraft/block/entity/EnchantingTableBlockEntity;FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/model/BookModel;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;II)V")) - private boolean wrapRender(BookModel instance, MatrixStack matrixStack, VertexConsumer vertexConsumer, int i, int j) { - return NoRender.INSTANCE.isDisabled() || !NoRender.getNoEnchantingTableBook(); - } +// @WrapWithCondition(method = "render(Lnet/minecraft/block/entity/EnchantingTableBlockEntity;FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/model/BookModel;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;II)V")) +// private boolean wrapRender(BookModel instance, MatrixStack matrixStack, VertexConsumer vertexConsumer, int i, int j) { +// return NoRender.INSTANCE.isDisabled() || !NoRender.getNoEnchantingTableBook(); +// } } diff --git a/src/main/java/com/lambda/mixin/render/MobSpawnerBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockEntity/MobSpawnerBlockEntityRendererMixin.java similarity index 69% rename from src/main/java/com/lambda/mixin/render/MobSpawnerBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockEntity/MobSpawnerBlockEntityRendererMixin.java index e2233bf31..a605fc29b 100644 --- a/src/main/java/com/lambda/mixin/render/MobSpawnerBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockEntity/MobSpawnerBlockEntityRendererMixin.java @@ -15,12 +15,16 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render; +package com.lambda.mixin.render.blockEntity; import com.lambda.module.modules.render.NoRender; import net.minecraft.block.entity.MobSpawnerBlockEntity; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.block.entity.MobSpawnerBlockEntityRenderer; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; +import net.minecraft.client.render.entity.EntityRenderManager; +import net.minecraft.client.render.entity.state.EntityRenderState; +import net.minecraft.client.render.state.CameraRenderState; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Mixin; @@ -30,8 +34,8 @@ @Mixin(MobSpawnerBlockEntityRenderer.class) public class MobSpawnerBlockEntityRendererMixin { - @Inject(method = "render(Lnet/minecraft/block/entity/MobSpawnerBlockEntity;FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/util/math/Vec3d;)V", at = @At("HEAD"), cancellable = true) - private void injectRender(MobSpawnerBlockEntity mobSpawnerBlockEntity, float f, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, int j, Vec3d vec3d, CallbackInfo ci) { + @Inject(method = "renderDisplayEntity", at = @At("HEAD"), cancellable = true) + private static void injectRender(MatrixStack matrices, OrderedRenderCommandQueue queue, EntityRenderState state, EntityRenderManager entityRenderDispatcher, float rotation, float scale, CameraRenderState cameraRenderState, CallbackInfo ci) { if (NoRender.INSTANCE.isEnabled() && NoRender.getNoSpawnerMob()) ci.cancel(); } } diff --git a/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java b/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java index d98e1efb3..5b86cf797 100644 --- a/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java +++ b/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java @@ -46,21 +46,21 @@ private void onRemoveEntity(int entityId, Entity.RemovalReason removalReason, Ca EventFlow.post(new EntityEvent.Removal(entity, removalReason)); } - @ModifyReturnValue(method = "getCloudsColor", at = @At("RETURN")) - private int modifyGetCloudsColor(int original) { - if (WorldColors.INSTANCE.isEnabled() && WorldColors.getCustomClouds()) { - return WorldColors.getCloudColor().getRGB() & 0xFFFFFF; - } - return original; - } - - @ModifyReturnValue(method = "getSkyColor", at = @At("RETURN")) - private int modifyGetSkyColor(int original) { - if (WorldColors.INSTANCE.isEnabled() && WorldColors.getCustomSky()) { - return WorldColors.getSkyColor().getRGB() & 0xFFFFFF; - } - return original; - } +// @ModifyReturnValue(method = "getCloudsColor", at = @At("RETURN")) +// private int modifyGetCloudsColor(int original) { +// if (WorldColors.INSTANCE.isEnabled() && WorldColors.getCustomClouds()) { +// return WorldColors.getCloudColor().getRGB() & 0xFFFFFF; +// } +// return original; +// } +// +// @ModifyReturnValue(method = "getSkyColor", at = @At("RETURN")) +// private int modifyGetSkyColor(int original) { +// if (WorldColors.INSTANCE.isEnabled() && WorldColors.getCustomSky()) { +// return WorldColors.getSkyColor().getRGB() & 0xFFFFFF; +// } +// return original; +// } @Inject(method = "handleBlockUpdate", at = @At("HEAD"), cancellable = true) diff --git a/src/main/resources/lambda.mixins.common.json b/src/main/resources/lambda.mixins.common.json index 6eeb82640..040e2fd6e 100644 --- a/src/main/resources/lambda.mixins.common.json +++ b/src/main/resources/lambda.mixins.common.json @@ -27,13 +27,13 @@ "network.HandshakeC2SPacketMixin", "network.LoginHelloC2SPacketMixin", "network.LoginKeyC2SPacketMixin", - "render.AbstractSignBlockEntityRendererMixin", + "render.blockEntity.AbstractSignBlockEntityRendererMixin", "render.AbstractTerrainRenderContextMixin", "render.ArmorFeatureRendererMixin", "render.BackgroundRendererMixin", "render.DarknessEffectFogMixin", - "render.BeaconBlockEntityRendererMixin", - "render.BlockEntityRenderDispatcherMixin", + "render.blockEntity.BeaconBlockEntityRendererMixin", + "render.blockEntity.BlockEntityRenderDispatcherMixin", "render.BlockMixin", "render.BlockModelRendererMixin", "render.BlockRenderManagerMixin", @@ -48,7 +48,7 @@ "render.DebugRendererMixin", "render.DrawContextMixin", "render.ElytraFeatureRendererMixin", - "render.EnchantingTableBlockEntityRendererMixin", + "render.blockEntity.EnchantingTableBlockEntityRendererMixin", "render.EntityRendererMixin", "render.FluidRendererMixin", "render.GameRendererMixin", @@ -60,7 +60,7 @@ "render.InGameOverlayRendererMixin", "render.LightmapTextureManagerMixin", "render.LivingEntityRendererMixin", - "render.MobSpawnerBlockEntityRendererMixin", + "render.blockEntity.MobSpawnerBlockEntityRendererMixin", "render.ParticleManagerMixin", "render.PlayerListHudMixin", "render.RenderLayersMixin", From 1c3bba72b1e979b19daef91fb019573c8cef69d7 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 17 Dec 2025 20:22:04 +0000 Subject: [PATCH 06/20] fix viewmodel mixins --- .../mixin/render/HeldItemRendererMixin.java | 118 +++++++++--------- 1 file changed, 57 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java b/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java index 6aacbb9f2..ef2d89872 100644 --- a/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java @@ -20,9 +20,10 @@ import com.google.common.base.MoreObjects; import com.lambda.Lambda; import com.lambda.module.modules.render.ViewModel; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.AbstractClientPlayerEntity; -import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.item.HeldItemRenderer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; @@ -33,7 +34,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(HeldItemRenderer.class) @@ -43,63 +43,59 @@ public class HeldItemRendererMixin { @Shadow private ItemStack offHand; @Shadow private float equipProgressMainHand; @Shadow private float equipProgressOffHand; -// -// @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderArmHoldingItem(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IFFLnet/minecraft/util/Arm;)V")) -// private void onRenderArmHoldingItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack itemStack, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { -// if (!ViewModel.INSTANCE.isEnabled()) return; -// -// ViewModel.INSTANCE.transform(itemStack, hand, matrices); -// } -// -// @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemDisplayContext;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", ordinal = 1)) -// private void onRenderFirstPersonItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack itemStack, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { -// if (!ViewModel.INSTANCE.isEnabled()) return; -// -// ViewModel.INSTANCE.transform(itemStack, hand, matrices); -// } -// -// @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 2), index = 0) -// private float modifyEquipProgressMainHand(float value) { -// if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; -// -// ViewModel config = ViewModel.INSTANCE; -// ItemStack currentStack = client.player.getMainHandStack(); -// if (config.getOldAnimations() && !config.getSwapAnimation()) { -// mainHand = currentStack; -// } -// -// float progress = config.getOldAnimations() ? 1 : (float) Math.pow(client.player.getAttackCooldownProgress(1), 3); -// -// return (ItemStack.areEqual(mainHand, currentStack) ? progress : 0) - equipProgressMainHand; -// } -// -// @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 3), index = 0) -// private float modifyEquipProgressOffHand(float value) { -// if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; -// -// ViewModel config = ViewModel.INSTANCE; -// -// ItemStack currentStack = client.player.getOffHandStack(); -// if (config.getOldAnimations() && !config.getSwapAnimation()) { -// offHand = currentStack; -// } -// -// return (ItemStack.areEqual(offHand, currentStack) ? 1 : 0) - equipProgressOffHand; -// } -// -// @ModifyVariable(method = "renderItem(FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/network/ClientPlayerEntity;I)V", at = @At(value = "STORE", ordinal = 0), index = 6) -// private float modifySwing(float swingProgress) { -// ViewModel config = ViewModel.INSTANCE; -// MinecraftClient mc = Lambda.getMc(); -// if (config.isDisabled() || mc.player == null) return swingProgress; -// Hand hand = MoreObjects.firstNonNull(mc.player.preferredHand, Hand.MAIN_HAND); -// -// if (hand == Hand.MAIN_HAND) { -// return swingProgress + config.getMainSwingProgress(); -// } else if (hand == Hand.OFF_HAND) { -// return swingProgress + config.getOffhandSwingProgress(); -// } -// -// return swingProgress; -// } + + @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemDisplayContext;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;I)V")) + private void injectRenderFirstPersonItem(AbstractClientPlayerEntity player, float tickProgress, float pitch, Hand hand, float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, OrderedRenderCommandQueue orderedRenderCommandQueue, int light, CallbackInfo ci) { + if (ViewModel.INSTANCE.isEnabled()) ViewModel.INSTANCE.transform(item, hand, matrices); + } + + @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderArmHoldingItem(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;IFFLnet/minecraft/util/Arm;)V")) + private void injectRenderArmHoldingItem(AbstractClientPlayerEntity player, float tickProgress, float pitch, Hand hand, float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, OrderedRenderCommandQueue orderedRenderCommandQueue, int light, CallbackInfo ci) { + if (ViewModel.INSTANCE.isEnabled()) ViewModel.INSTANCE.transform(item, hand, matrices); + } + + @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 2), index = 0) + private float modifyEquipProgressMainHand(float value) { + if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; + + ViewModel config = ViewModel.INSTANCE; + ItemStack currentStack = client.player.getMainHandStack(); + if (config.getOldAnimations() && !config.getSwapAnimation()) { + mainHand = currentStack; + } + + float progress = config.getOldAnimations() ? 1 : (float) Math.pow(client.player.getAttackCooldownProgress(1), 3); + + return (ItemStack.areEqual(mainHand, currentStack) ? progress : 0) - equipProgressMainHand; + } + + @ModifyArg(method = "updateHeldItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;clamp(FFF)F", ordinal = 3), index = 0) + private float modifyEquipProgressOffHand(float value) { + if (client.player == null || ViewModel.INSTANCE.isDisabled()) return value; + + ViewModel config = ViewModel.INSTANCE; + + ItemStack currentStack = client.player.getOffHandStack(); + if (config.getOldAnimations() && !config.getSwapAnimation()) { + offHand = currentStack; + } + + return (ItemStack.areEqual(offHand, currentStack) ? 1 : 0) - equipProgressOffHand; + } + + @ModifyExpressionValue(method = "renderItem(FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;Lnet/minecraft/client/network/ClientPlayerEntity;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getHandSwingProgress(F)F")) + private float modifySwing(float swingProgress) { + ViewModel config = ViewModel.INSTANCE; + MinecraftClient mc = Lambda.getMc(); + if (config.isDisabled() || mc.player == null) return swingProgress; + Hand hand = MoreObjects.firstNonNull(mc.player.preferredHand, Hand.MAIN_HAND); + + if (hand == Hand.MAIN_HAND) { + return swingProgress + config.getMainSwingProgress(); + } else if (hand == Hand.OFF_HAND) { + return swingProgress + config.getOffhandSwingProgress(); + } + + return swingProgress; + } } From 988e40cf72efc0dd8b74436053288bfd83f29fbe Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 17 Dec 2025 23:45:50 +0100 Subject: [PATCH 07/20] Fixed blurry overlay for GUI --- src/main/kotlin/com/lambda/gui/LambdaScreen.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/com/lambda/gui/LambdaScreen.kt b/src/main/kotlin/com/lambda/gui/LambdaScreen.kt index cb35fc93f..7995a1612 100644 --- a/src/main/kotlin/com/lambda/gui/LambdaScreen.kt +++ b/src/main/kotlin/com/lambda/gui/LambdaScreen.kt @@ -26,4 +26,5 @@ object LambdaScreen : Screen(Text.of("Lambda")) { override fun shouldPause() = false override fun removed() = ClickGuiLayout.close() override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, deltaTicks: Float) {} + override fun renderBackground(context: DrawContext?, mouseX: Int, mouseY: Int, deltaTicks: Float) {} } From 984b26c70e5f1039cbd5f6dfd136c1e6b2f91edf Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 17 Dec 2025 23:51:24 +0100 Subject: [PATCH 08/20] XRay crash notice --- src/main/kotlin/com/lambda/module/modules/render/XRay.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/render/XRay.kt b/src/main/kotlin/com/lambda/module/modules/render/XRay.kt index 5fc7ed8d3..4be15ef08 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/XRay.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/XRay.kt @@ -41,12 +41,14 @@ object XRay : Module( ) @JvmStatic - val opacity by setting("Opacity", 40, 0..100, 1, "Opacity of the non x-rayed blocks, (automatically overridden as 0 when running Sodium)") + val opacity by setting("Opacity", 40, 1..100, 1, "Opacity of the non x-rayed blocks, (automatically overridden as 0 when running Sodium)") .onValueChange { _, _ -> if (isEnabled) mc.worldRenderer.reload() } - private val selection by setting("Block Selection", defaultBlocks, description = "Block selection that will be shown (whitelist) or hidden (blacklist)") .onValueChange { _, _ -> if (isEnabled) mc.worldRenderer.reload() } + + // ToDo: Blacklist causes huge performance issues due to many single faces being rendered private val mode by setting("Selection Mode", Selection.Whitelist, "The mode of the block selection") + .onValueChange { _, _ -> if (isEnabled) mc.worldRenderer.reload() } @JvmStatic fun isSelected(blockState: BlockState) = mode.select(blockState) From b2932a78493760c98662820c888c44ab0427a5e6 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 18 Dec 2025 01:43:22 +0100 Subject: [PATCH 09/20] Fix ImGUI layering --- .../com/lambda/mixin/render/GameRendererMixin.java | 6 ++++++ .../java/com/lambda/mixin/render/InGameHudMixin.java | 7 ------- src/main/kotlin/com/lambda/gui/LambdaScreen.kt | 11 ++++++++++- .../com/lambda/gui/components/ClickGuiLayout.kt | 2 ++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/lambda/mixin/render/GameRendererMixin.java b/src/main/java/com/lambda/mixin/render/GameRendererMixin.java index a9386af93..6f8bb492a 100644 --- a/src/main/java/com/lambda/mixin/render/GameRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/GameRendererMixin.java @@ -20,6 +20,7 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.RenderEvent; import com.lambda.graphics.RenderMain; +import com.lambda.gui.DearImGui; import com.lambda.module.modules.render.NoRender; import com.lambda.module.modules.render.Zoom; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; @@ -80,4 +81,9 @@ private void injectShowFloatingItem(ItemStack floatingItem, CallbackInfo ci) { private float modifyGetFov(float original) { return original / Zoom.getLerpedZoom(); } + + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/render/GuiRenderer;render(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;)V", shift = At.Shift.AFTER)) + private void onGuiRenderComplete(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) { + DearImGui.INSTANCE.render(); + } } diff --git a/src/main/java/com/lambda/mixin/render/InGameHudMixin.java b/src/main/java/com/lambda/mixin/render/InGameHudMixin.java index 407ef5998..d6ca4f2ca 100644 --- a/src/main/java/com/lambda/mixin/render/InGameHudMixin.java +++ b/src/main/java/com/lambda/mixin/render/InGameHudMixin.java @@ -35,13 +35,6 @@ @Mixin(InGameHud.class) public class InGameHudMixin { - /** - * Begins our 2d render after the game has rendered all 2d elements - */ - @Inject(method = "render", at = @At("TAIL")) - private void onRender(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) { - DearImGui.INSTANCE.render(); - } @Inject(method = "renderNauseaOverlay", at = @At("HEAD"), cancellable = true) private void injectRenderNauseaOverlay(DrawContext context, float nauseaStrength, CallbackInfo ci) { diff --git a/src/main/kotlin/com/lambda/gui/LambdaScreen.kt b/src/main/kotlin/com/lambda/gui/LambdaScreen.kt index 7995a1612..bb01781ea 100644 --- a/src/main/kotlin/com/lambda/gui/LambdaScreen.kt +++ b/src/main/kotlin/com/lambda/gui/LambdaScreen.kt @@ -26,5 +26,14 @@ object LambdaScreen : Screen(Text.of("Lambda")) { override fun shouldPause() = false override fun removed() = ClickGuiLayout.close() override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, deltaTicks: Float) {} - override fun renderBackground(context: DrawContext?, mouseX: Int, mouseY: Int, deltaTicks: Float) {} + + override fun applyBlur(context: DrawContext?) { + if (!ClickGuiLayout.backgroundBlur) return + super.applyBlur(context) + } + + override fun renderDarkening(context: DrawContext?) { + if (!ClickGuiLayout.backgroundDarkening) return + super.renderDarkening(context) + } } diff --git a/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt b/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt index ced5ec71b..89729a4e3 100644 --- a/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt +++ b/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt @@ -122,6 +122,8 @@ object ClickGuiLayout : Loadable, Configurable(GuiConfig) { @JvmStatic val setLambdaWindowTitle by setting("Set Lambda Window Title", true).onValueChange { _, _ -> mc.updateWindowTitle() }.group(Group.General) val lambdaTitleAppendixName by setting("Append Username", true) { setLambdaWindowTitle }.onValueChange { _, _ -> mc.updateWindowTitle() }.group(Group.General) + val backgroundBlur by setting("Background Blur", true).group(Group.General) + val backgroundDarkening by setting("Background Darkening", true).group(Group.General) // Snapping val snapEnabled by setting("Enable Snapping", true, "Master toggle for GUI/HUD snapping").group(Group.Snapping) From e81c6ff986cd80dd9e26b29c3fca7df1e2e58a66 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 18 Dec 2025 04:07:52 +0100 Subject: [PATCH 10/20] Updated to Baritone 1.21.11 thanks to rfresh --- build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1582e2065..ef77a9015 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -175,8 +175,7 @@ dependencies { includeLib("io.ktor:ktor-serialization-gson:$ktorVersion") // Add mods - /*modImplementation("com.github.rfresh2:baritone-fabric:$minecraftVersion") */ - modCompileOnly("com.github.rfresh2:baritone-fabric:1.21.10-SNAPSHOT") // ToDo: Move to 1.21.11 + modCompileOnly("com.github.rfresh2:baritone-fabric:$minecraftVersion-SNAPSHOT") modCompileOnly("maven.modrinth:sodium:$sodiumVersion") modCompileOnly("maven.modrinth:malilib:$maLiLibVersion") modCompileOnly("maven.modrinth:litematica:$litematicaVersion") From 440e171c6ee00be63d5e4af99f2dde43d43ea78e Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Thu, 18 Dec 2025 06:17:18 +0000 Subject: [PATCH 11/20] no render mixin fixes --- .../lambda/mixin/render/DebugHudMixin.java | 6 -- .../mixin/render/ParticleManagerMixin.java | 60 ------------------- .../AbstractSignBlockEntityRendererMixin.java | 2 +- .../BeaconBlockEntityRendererMixin.java | 2 +- .../BlockEntityRenderDispatcherMixin.java | 2 +- ...chantingTableBlockEntityRendererMixin.java | 10 ++-- .../MobSpawnerBlockEntityRendererMixin.java | 5 +- .../particle/BillboardParticleMixin.java | 36 +++++++++++ .../ElderGuardianParticleRendererMixin.java | 38 ++++++++++++ .../ItemPickupParticleRendererMixin.java | 38 ++++++++++++ .../lambda/module/modules/combat/KillAura.kt | 9 --- .../lambda/module/modules/render/NoRender.kt | 4 ++ .../lambda/module/modules/render/ViewModel.kt | 37 +++++------- src/main/resources/lambda.accesswidener | 1 + src/main/resources/lambda.mixins.common.json | 24 ++++---- 15 files changed, 153 insertions(+), 121 deletions(-) delete mode 100644 src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java rename src/main/java/com/lambda/mixin/render/{blockEntity => blockentity}/AbstractSignBlockEntityRendererMixin.java (97%) rename src/main/java/com/lambda/mixin/render/{blockEntity => blockentity}/BeaconBlockEntityRendererMixin.java (97%) rename src/main/java/com/lambda/mixin/render/{blockEntity => blockentity}/BlockEntityRenderDispatcherMixin.java (97%) rename src/main/java/com/lambda/mixin/render/{blockEntity => blockentity}/EnchantingTableBlockEntityRendererMixin.java (67%) rename src/main/java/com/lambda/mixin/render/{blockEntity => blockentity}/MobSpawnerBlockEntityRendererMixin.java (90%) create mode 100644 src/main/java/com/lambda/mixin/render/particle/BillboardParticleMixin.java create mode 100644 src/main/java/com/lambda/mixin/render/particle/ElderGuardianParticleRendererMixin.java create mode 100644 src/main/java/com/lambda/mixin/render/particle/ItemPickupParticleRendererMixin.java diff --git a/src/main/java/com/lambda/mixin/render/DebugHudMixin.java b/src/main/java/com/lambda/mixin/render/DebugHudMixin.java index 28c47db4b..3c0a42c54 100644 --- a/src/main/java/com/lambda/mixin/render/DebugHudMixin.java +++ b/src/main/java/com/lambda/mixin/render/DebugHudMixin.java @@ -17,14 +17,8 @@ package com.lambda.mixin.render; -import com.lambda.util.DebugInfoHud; import net.minecraft.client.gui.hud.DebugHud; 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; - -import java.util.List; @Mixin(DebugHud.class) public class DebugHudMixin { diff --git a/src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java b/src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java deleted file mode 100644 index f5e56cd3f..000000000 --- a/src/main/java/com/lambda/mixin/render/ParticleManagerMixin.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.mixin.render; - -import com.lambda.module.modules.render.NoRender; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.client.particle.Particle; -import net.minecraft.client.particle.ParticleManager; -import net.minecraft.client.particle.ParticleTextureSheet; -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.VertexConsumerProvider; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.LinkedList; -import java.util.Queue; -import java.util.stream.Collectors; - -@Mixin(ParticleManager.class) -public class ParticleManagerMixin { - // prevents the particles from being stored and potential overhead. Downside being they need to spawn back in rather than just enabling rendering again -// @Inject(method = "addParticle(Lnet/minecraft/client/particle/Particle;)V", at = @At("HEAD"), cancellable = true) -// private void injectAddParticle(Particle particle, CallbackInfo ci) { -// if (NoRender.shouldOmitParticle(particle)) ci.cancel(); -// } - -// @WrapOperation(method = "renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleManager;renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/particle/ParticleTextureSheet;Ljava/util/Queue;)V")) -// private void wrapRenderParticles(Camera camera, float tickProgress, VertexConsumerProvider.Immediate vertexConsumers, ParticleTextureSheet sheet, Queue particles, Operation original) { -// original.call(camera, tickProgress, vertexConsumers, sheet, filterParticles(particles)); -// } -// -// @WrapOperation(method = "renderParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleManager;renderCustomParticles(Lnet/minecraft/client/render/Camera;FLnet/minecraft/client/render/VertexConsumerProvider$Immediate;Ljava/util/Queue;)V")) -// private void wrapRenderParticles(Camera camera, float tickProgress, VertexConsumerProvider.Immediate vertexConsumers, Queue particles, Operation original) { -// original.call(camera, tickProgress, vertexConsumers, filterParticles(particles)); -// } - - @Unique - private Queue filterParticles(Queue particles) { - return particles.stream().filter(particle -> - !NoRender.shouldOmitParticle(particle)).collect(Collectors.toCollection(LinkedList::new) - ); - } -} diff --git a/src/main/java/com/lambda/mixin/render/blockEntity/AbstractSignBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockentity/AbstractSignBlockEntityRendererMixin.java similarity index 97% rename from src/main/java/com/lambda/mixin/render/blockEntity/AbstractSignBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockentity/AbstractSignBlockEntityRendererMixin.java index bd3f93ac6..9d6232c03 100644 --- a/src/main/java/com/lambda/mixin/render/blockEntity/AbstractSignBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockentity/AbstractSignBlockEntityRendererMixin.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render.blockEntity; +package com.lambda.mixin.render.blockentity; import com.lambda.module.modules.render.NoRender; import net.minecraft.client.render.block.entity.AbstractSignBlockEntityRenderer; diff --git a/src/main/java/com/lambda/mixin/render/blockEntity/BeaconBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockentity/BeaconBlockEntityRendererMixin.java similarity index 97% rename from src/main/java/com/lambda/mixin/render/blockEntity/BeaconBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockentity/BeaconBlockEntityRendererMixin.java index d86cf5493..8ad9a6d83 100644 --- a/src/main/java/com/lambda/mixin/render/blockEntity/BeaconBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockentity/BeaconBlockEntityRendererMixin.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render.blockEntity; +package com.lambda.mixin.render.blockentity; import com.lambda.module.modules.render.NoRender; import net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer; diff --git a/src/main/java/com/lambda/mixin/render/blockEntity/BlockEntityRenderDispatcherMixin.java b/src/main/java/com/lambda/mixin/render/blockentity/BlockEntityRenderDispatcherMixin.java similarity index 97% rename from src/main/java/com/lambda/mixin/render/blockEntity/BlockEntityRenderDispatcherMixin.java rename to src/main/java/com/lambda/mixin/render/blockentity/BlockEntityRenderDispatcherMixin.java index 6d9a5523a..784faeee0 100644 --- a/src/main/java/com/lambda/mixin/render/blockEntity/BlockEntityRenderDispatcherMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockentity/BlockEntityRenderDispatcherMixin.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render.blockEntity; +package com.lambda.mixin.render.blockentity; import com.lambda.module.modules.render.NoRender; import net.minecraft.block.entity.BlockEntity; diff --git a/src/main/java/com/lambda/mixin/render/blockEntity/EnchantingTableBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockentity/EnchantingTableBlockEntityRendererMixin.java similarity index 67% rename from src/main/java/com/lambda/mixin/render/blockEntity/EnchantingTableBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockentity/EnchantingTableBlockEntityRendererMixin.java index 8046aac2f..f0e5b02dd 100644 --- a/src/main/java/com/lambda/mixin/render/blockEntity/EnchantingTableBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockentity/EnchantingTableBlockEntityRendererMixin.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render.blockEntity; +package com.lambda.mixin.render.blockentity; import com.lambda.module.modules.render.NoRender; import net.minecraft.client.render.block.entity.EnchantingTableBlockEntityRenderer; @@ -30,8 +30,8 @@ @Mixin(EnchantingTableBlockEntityRenderer.class) public class EnchantingTableBlockEntityRendererMixin { -// @WrapWithCondition(method = "render(Lnet/minecraft/block/entity/EnchantingTableBlockEntity;FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/model/BookModel;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;II)V")) -// private boolean wrapRender(BookModel instance, MatrixStack matrixStack, VertexConsumer vertexConsumer, int i, int j) { -// return NoRender.INSTANCE.isDisabled() || !NoRender.getNoEnchantingTableBook(); -// } + @Inject(method = "render(Lnet/minecraft/client/render/block/entity/state/EnchantingTableBlockEntityRenderState;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;Lnet/minecraft/client/render/state/CameraRenderState;)V", at = @At("HEAD"), cancellable = true) + private void injectRender(EnchantingTableBlockEntityRenderState renderState, MatrixStack matrices, OrderedRenderCommandQueue queue, CameraRenderState cameraRenderState, CallbackInfo ci) { + if (NoRender.INSTANCE.isEnabled() && NoRender.getNoEnchantingTableBook()) ci.cancel(); + } } diff --git a/src/main/java/com/lambda/mixin/render/blockEntity/MobSpawnerBlockEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/blockentity/MobSpawnerBlockEntityRendererMixin.java similarity index 90% rename from src/main/java/com/lambda/mixin/render/blockEntity/MobSpawnerBlockEntityRendererMixin.java rename to src/main/java/com/lambda/mixin/render/blockentity/MobSpawnerBlockEntityRendererMixin.java index a605fc29b..f47e9d2c0 100644 --- a/src/main/java/com/lambda/mixin/render/blockEntity/MobSpawnerBlockEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/blockentity/MobSpawnerBlockEntityRendererMixin.java @@ -15,18 +15,15 @@ * along with this program. If not, see . */ -package com.lambda.mixin.render.blockEntity; +package com.lambda.mixin.render.blockentity; import com.lambda.module.modules.render.NoRender; -import net.minecraft.block.entity.MobSpawnerBlockEntity; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.block.entity.MobSpawnerBlockEntityRenderer; import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.EntityRenderManager; import net.minecraft.client.render.entity.state.EntityRenderState; import net.minecraft.client.render.state.CameraRenderState; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/java/com/lambda/mixin/render/particle/BillboardParticleMixin.java b/src/main/java/com/lambda/mixin/render/particle/BillboardParticleMixin.java new file mode 100644 index 000000000..06faa1a5d --- /dev/null +++ b/src/main/java/com/lambda/mixin/render/particle/BillboardParticleMixin.java @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.mixin.render.particle; + +import com.lambda.module.modules.render.NoRender; +import net.minecraft.client.particle.BillboardParticle; +import net.minecraft.client.particle.BillboardParticleSubmittable; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.render.Camera; +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(BillboardParticle.class) +public class BillboardParticleMixin { + @Inject(method = "render(Lnet/minecraft/client/particle/BillboardParticleSubmittable;Lnet/minecraft/client/render/Camera;F)V", at = @At("HEAD"), cancellable = true) + private void injectRender(BillboardParticleSubmittable submittable, Camera camera, float tickDelta, CallbackInfo ci) { + if (NoRender.shouldOmitParticle((Particle) ((Object) this))) ci.cancel(); + } +} diff --git a/src/main/java/com/lambda/mixin/render/particle/ElderGuardianParticleRendererMixin.java b/src/main/java/com/lambda/mixin/render/particle/ElderGuardianParticleRendererMixin.java new file mode 100644 index 000000000..cc7b2e818 --- /dev/null +++ b/src/main/java/com/lambda/mixin/render/particle/ElderGuardianParticleRendererMixin.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.mixin.render.particle; + +import com.lambda.module.modules.render.NoRender; +import net.minecraft.client.particle.ElderGuardianParticle; +import net.minecraft.client.particle.ElderGuardianParticleRenderer; +import net.minecraft.client.particle.NoRenderParticleRenderer; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.Submittable; +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(ElderGuardianParticleRenderer.class) +public class ElderGuardianParticleRendererMixin { + @Inject(method = "render", at = @At("HEAD"), cancellable = true) + private void injectRender(Frustum frustum, Camera camera, float tickProgress, CallbackInfoReturnable cir) { + if (NoRender.shouldOmitParticle(ElderGuardianParticle.class)) cir.setReturnValue(NoRenderParticleRenderer.EMPTY); + } +} diff --git a/src/main/java/com/lambda/mixin/render/particle/ItemPickupParticleRendererMixin.java b/src/main/java/com/lambda/mixin/render/particle/ItemPickupParticleRendererMixin.java new file mode 100644 index 000000000..509b3323c --- /dev/null +++ b/src/main/java/com/lambda/mixin/render/particle/ItemPickupParticleRendererMixin.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.mixin.render.particle; + +import com.lambda.module.modules.render.NoRender; +import net.minecraft.client.particle.ItemPickupParticle; +import net.minecraft.client.particle.ItemPickupParticleRenderer; +import net.minecraft.client.particle.NoRenderParticleRenderer; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.Submittable; +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(ItemPickupParticleRenderer.class) +public class ItemPickupParticleRendererMixin { + @Inject(method = "render", at = @At("HEAD"), cancellable = true) + private void injectRender(Frustum frustum, Camera camera, float tickProgress, CallbackInfoReturnable cir) { + if (NoRender.shouldOmitParticle(ItemPickupParticle.class)) cir.setReturnValue(NoRenderParticleRenderer.EMPTY); + } +} diff --git a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index bcd8d1d4e..95d38cef4 100644 --- a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -41,7 +41,6 @@ import com.lambda.util.player.SlotUtils.hotbarAndStorage import com.lambda.util.world.raycast.RayCastUtils.entityResult import net.minecraft.entity.LivingEntity import net.minecraft.util.Hand -import net.minecraft.util.math.Vec3d object KillAura : Module( name = "KillAura", @@ -61,16 +60,12 @@ object KillAura : Module( val target: LivingEntity? get() = targeting.target() - private var shakeRandom = Vec3d.ZERO - private var speedMultiplier = 1.0 - private var lastAttackTime = 0L private var hitDelay = 100.0 private var prevY = 0.0 private var lastY = 0.0 private var lastOnGround = true - private var onGroundTicks = 0 enum class Group(override val displayName: String) : NamedEnum { Build("Build"), @@ -145,13 +140,9 @@ object KillAura : Module( } private fun reset() { - speedMultiplier = 1.0 - shakeRandom = Vec3d.ZERO - lastY = 0.0 prevY = 0.0 lastOnGround = true - onGroundTicks = 0 lastAttackTime = 0L hitDelay = 100.0 diff --git a/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt b/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt index 70af2f42c..e0f576482 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/NoRender.kt @@ -147,6 +147,10 @@ object NoRender : Module( fun shouldOmitParticle(particle: Particle) = isEnabled && particleMap[particle.javaClass.simpleName] in particles + @JvmStatic + fun shouldOmitParticle(particle: Class) = + isEnabled && particleMap[particle.simpleName] in particles + @JvmStatic fun shouldOmitEntity(entity: Entity): Boolean { val simpleName = entity.javaClass.simpleName diff --git a/src/main/kotlin/com/lambda/module/modules/render/ViewModel.kt b/src/main/kotlin/com/lambda/module/modules/render/ViewModel.kt index e51c6f497..6a2a67594 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/ViewModel.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/ViewModel.kt @@ -36,6 +36,7 @@ import org.joml.Vector3f import org.joml.Vector3i import kotlin.math.tan +@Suppress("unused") object ViewModel : Module( name = "ViewModel", description = "Adjusts hand and held item rendering", @@ -115,17 +116,13 @@ object ViewModel : Module( listen { event -> if (event.keyCode == mc.options.attackKey.boundKey.code) { - if (event.isPressed) { - attackKeyTicksPressed = 0 - } else if (event.isReleased) { - attackKeyTicksPressed = -1 - } + if (event.isPressed) attackKeyTicksPressed = 0 + else if (event.isReleased) attackKeyTicksPressed = -1 } } listen { - if (attackKeyTicksPressed != -1) - attackKeyTicksPressed++ + if (attackKeyTicksPressed != -1) attackKeyTicksPressed++ } } @@ -158,20 +155,16 @@ object ViewModel : Module( val matrix = matrices.peek().positionMatrix - val distance = if (emptyHand) { - handFovAnchorDistance - } else { - when (side) { - Side.Left -> leftFovAnchorDistance - Side.Right -> rightFovAnchorDistance - } + val distance = if (emptyHand) handFovAnchorDistance + else when (side) { + Side.Left -> leftFovAnchorDistance + Side.Right -> rightFovAnchorDistance } - val warpMatrix = Matrix4f().apply { - translate(0f, 0f, -distance) - scale(1f, 1f, fovRatio) - translate(0f, 0f, distance) - } + val warpMatrix = Matrix4f() + .translate(0f, 0f, -distance) + .scale(1f, 1f, fovRatio) + .translate(0f, 0f, distance) matrix.mul(warpMatrix) } @@ -212,14 +205,12 @@ object ViewModel : Module( private fun getRotationVec(side: Side, emptyHand: Boolean) = when (side) { - Side.Left -> { + Side.Left -> if (emptyHand) Vector3i(handXRotation, -handYRotation, -handZRotation) else Vector3i(leftXRotation, -leftYRotation, -leftZRotation) - } - Side.Right -> { + Side.Right -> if (emptyHand) Vector3i(handXRotation, handYRotation, handZRotation) else Vector3i(rightXRotation, rightYRotation, rightZRotation) - } } fun adjustSwing(hand: Hand, player: AbstractClientPlayerEntity) = diff --git a/src/main/resources/lambda.accesswidener b/src/main/resources/lambda.accesswidener index b1ab3eb4c..fa5cb8b77 100644 --- a/src/main/resources/lambda.accesswidener +++ b/src/main/resources/lambda.accesswidener @@ -28,6 +28,7 @@ transitive-accessible method net/minecraft/item/BlockItem getPlaceSound (Lnet/mi transitive-accessible field net/minecraft/state/State owner Ljava/lang/Object; # MOVED in 1.21.11: BackgroundRenderer$StatusEffectFogModifier -> net/minecraft/client/render/fog/StatusEffectFogModifier transitive-accessible class net/minecraft/client/render/fog/StatusEffectFogModifier +transitive-accessible field net/minecraft/client/particle/NoRenderParticleRenderer EMPTY Lnet/minecraft/client/render/Submittable; # Entity transitive-accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; diff --git a/src/main/resources/lambda.mixins.common.json b/src/main/resources/lambda.mixins.common.json index 040e2fd6e..6c6be1c70 100644 --- a/src/main/resources/lambda.mixins.common.json +++ b/src/main/resources/lambda.mixins.common.json @@ -6,6 +6,8 @@ "client": [ "CrashReportMixin", "MinecraftClientMixin", + "baritone.MixinBaritonePlayerContext", + "baritone.MixinLookBehavior", "client.sound.SoundSystemMixin", "entity.ClientPlayerEntityMixin", "entity.ClientPlayInteractionManagerMixin", @@ -27,13 +29,9 @@ "network.HandshakeC2SPacketMixin", "network.LoginHelloC2SPacketMixin", "network.LoginKeyC2SPacketMixin", - "render.blockEntity.AbstractSignBlockEntityRendererMixin", "render.AbstractTerrainRenderContextMixin", "render.ArmorFeatureRendererMixin", "render.BackgroundRendererMixin", - "render.DarknessEffectFogMixin", - "render.blockEntity.BeaconBlockEntityRendererMixin", - "render.blockEntity.BlockEntityRenderDispatcherMixin", "render.BlockMixin", "render.BlockModelRendererMixin", "render.BlockRenderManagerMixin", @@ -44,24 +42,22 @@ "render.ChatInputSuggestorMixin", "render.ChatScreenMixin", "render.ChunkOcclusionDataBuilderMixin", + "render.DarknessEffectFogMixin", "render.DebugHudMixin", "render.DebugRendererMixin", "render.DrawContextMixin", "render.ElytraFeatureRendererMixin", - "render.blockEntity.EnchantingTableBlockEntityRendererMixin", "render.EntityRendererMixin", "render.FluidRendererMixin", "render.GameRendererMixin", - "render.HandledScreenMixin", "render.GlStateManagerMixin", + "render.HandledScreenMixin", "render.HeadFeatureRendererMixin", "render.HeldItemRendererMixin", "render.InGameHudMixin", "render.InGameOverlayRendererMixin", "render.LightmapTextureManagerMixin", "render.LivingEntityRendererMixin", - "render.blockEntity.MobSpawnerBlockEntityRendererMixin", - "render.ParticleManagerMixin", "render.PlayerListHudMixin", "render.RenderLayersMixin", "render.ScreenHandlerMixin", @@ -76,14 +72,20 @@ "render.WeatherRenderingMixin", "render.WorldBorderRenderingMixin", "render.WorldRendererMixin", + "render.blockentity.AbstractSignBlockEntityRendererMixin", + "render.blockentity.BeaconBlockEntityRendererMixin", + "render.blockentity.BlockEntityRenderDispatcherMixin", + "render.blockentity.EnchantingTableBlockEntityRendererMixin", + "render.blockentity.MobSpawnerBlockEntityRendererMixin", + "render.particle.BillboardParticleMixin", + "render.particle.ElderGuardianParticleRendererMixin", + "render.particle.ItemPickupParticleRendererMixin", "world.AbstractBlockMixin", "world.BlockCollisionSpliteratorMixin", "world.ClientChunkManagerMixin", "world.ClientWorldMixin", "world.StructureTemplateMixin", - "world.WorldMixin", - "baritone.MixinBaritonePlayerContext", - "baritone.MixinLookBehavior" + "world.WorldMixin" ], "injectors": { "defaultRequire": 0 From 6f7a07d8775ce5403b51f890ee16ab67d023e887 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 19 Dec 2025 05:14:52 +0100 Subject: [PATCH 12/20] New Lambda RenderPipeline using native MC renderer - Solved precision issues with ESP - Using MC new render API --- .../mixin/render/GameRendererMixin.java | 26 +- .../com/lambda/event/events/RenderEvent.kt | 7 +- .../kotlin/com/lambda/graphics/RenderMain.kt | 47 +- .../com/lambda/graphics/gl/GlStateUtils.kt | 1 + .../lambda/graphics/mc/ChunkedRegionESP.kt | 312 ++++++++++ .../graphics/mc/LambdaRenderPipelines.kt | 164 ++++++ .../com/lambda/graphics/mc/RegionRenderer.kt | 142 +++++ .../lambda/graphics/mc/RegionShapeBuilder.kt | 338 +++++++++++ .../graphics/mc/RegionVertexCollector.kt | 168 ++++++ .../com/lambda/graphics/mc/RenderRegion.kt | 60 ++ .../lambda/graphics/mc/TransientRegionESP.kt | 127 +++++ .../graphics/renderer/esp/ChunkedESP.kt | 115 ---- .../lambda/graphics/renderer/esp/ShapeDsl.kt | 532 +++++++----------- .../com/lambda/graphics/renderer/esp/Treed.kt | 81 --- .../lambda/module/modules/render/BlockESP.kt | 29 +- 15 files changed, 1561 insertions(+), 588 deletions(-) create mode 100644 src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt delete mode 100644 src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt delete mode 100644 src/main/kotlin/com/lambda/graphics/renderer/esp/Treed.kt diff --git a/src/main/java/com/lambda/mixin/render/GameRendererMixin.java b/src/main/java/com/lambda/mixin/render/GameRendererMixin.java index 6f8bb492a..98cf293e7 100644 --- a/src/main/java/com/lambda/mixin/render/GameRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/GameRendererMixin.java @@ -27,6 +27,7 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.blaze3d.buffers.GpuBufferSlice; import net.minecraft.client.render.Camera; import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.RenderTickCounter; @@ -34,6 +35,7 @@ import net.minecraft.client.util.ObjectAllocator; import net.minecraft.item.ItemStack; import org.joml.Matrix4f; +import org.joml.Vector4f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -48,24 +50,12 @@ private void updateTargetedEntityInvoke(float tickDelta, CallbackInfo info) { } } - /** - * Begins our 3d render after the game has rendered the world - *
{@code
-     * float m = Math.max(h, (float)(Integer)this.client.options.getFov().getValue());
-     * Matrix4f matrix4f2 = this.getBasicProjectionMatrix(m);
-     * RenderSystem.setProjectionMatrix(matrix4f, ProjectionType.PERSPECTIVE);
-     * Quaternionf quaternionf = camera.getRotation().conjugate(new Quaternionf());
-     * Matrix4f matrix4f3 = (new Matrix4f()).rotation(quaternionf);
-     * this.client.worldRenderer.setupFrustum(camera.getPos(), matrix4f3, matrix4f2);
-     * this.client.worldRenderer.render(this.pool, renderTickCounter, bl, camera, this, matrix4f3, matrix4f);
-     * }
- */ -// @WrapOperation(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;render(Lnet/minecraft/client/util/ObjectAllocator;Lnet/minecraft/client/render/RenderTickCounter;ZLnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/GameRenderer;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V")) -// void onRenderWorld(WorldRenderer instance, ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, Operation original) { -// original.call(instance, allocator, tickCounter, renderBlockOutline, camera, gameRenderer, positionMatrix, projectionMatrix); -// -// RenderMain.render3D(positionMatrix, projectionMatrix); -// } + @WrapOperation(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;render(Lnet/minecraft/client/util/ObjectAllocator;Lnet/minecraft/client/render/RenderTickCounter;ZLnet/minecraft/client/render/Camera;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lorg/joml/Vector4f;Z)V")) + void onRenderWorld(WorldRenderer instance, ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, Matrix4f positionMatrix, Matrix4f basicProjectionMatrix, Matrix4f projectionMatrix, GpuBufferSlice fogBuffer, Vector4f fogColor, boolean renderSky, Operation original) { + original.call(instance, allocator, tickCounter, renderBlockOutline, camera, positionMatrix, basicProjectionMatrix, projectionMatrix, fogBuffer, fogColor, renderSky); + + RenderMain.render3D(positionMatrix, projectionMatrix); + } @ModifyExpressionValue(method = "renderWorld", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0)) private float modifyMax(float original) { diff --git a/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/src/main/kotlin/com/lambda/event/events/RenderEvent.kt index 812979bfa..20a4b516d 100644 --- a/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -22,13 +22,14 @@ import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.RenderMain import com.lambda.graphics.renderer.esp.ShapeBuilder -import com.lambda.graphics.renderer.esp.Treed fun Any.onStaticRender(block: SafeContext.(ShapeBuilder) -> Unit) = - listen { block(ShapeBuilder(Treed.Static.faceBuilder, Treed.Static.edgeBuilder)) } + listen { block(ShapeBuilder(RenderMain.StaticESP)) } + fun Any.onDynamicRender(block: SafeContext.(ShapeBuilder) -> Unit) = - listen { block(ShapeBuilder(Treed.Dynamic.faceBuilder, Treed.Dynamic.edgeBuilder)) } + listen { block(ShapeBuilder(RenderMain.DynamicESP)) } sealed class RenderEvent { object Upload : Event diff --git a/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 02ff3bca7..bbd9b7c14 100644 --- a/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -17,60 +17,47 @@ package com.lambda.graphics -import com.lambda.Lambda.mc import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.gl.GlStateUtils.setupGL import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrices -import com.lambda.graphics.renderer.esp.Treed -import com.lambda.util.math.Vec2d -import com.mojang.blaze3d.opengl.GlStateManager -import com.mojang.blaze3d.systems.RenderSystem -import net.minecraft.client.gl.GlBackend -import net.minecraft.client.texture.GlTexture +import com.lambda.graphics.mc.LambdaRenderPipelines +import com.lambda.graphics.mc.TransientRegionESP import org.joml.Matrix4f -import org.lwjgl.opengl.GL30.GL_FRAMEBUFFER object RenderMain { - val projectionMatrix = Matrix4f() - val modelViewMatrix get() = Matrices.peek() - val projModel: Matrix4f get() = Matrix4f(projectionMatrix).mul(modelViewMatrix) + val StaticESP = TransientRegionESP("Static") + val DynamicESP = TransientRegionESP("Dynamic") - var screenSize = Vec2d.ZERO + val projectionMatrix = Matrix4f() + val modelViewMatrix + get() = Matrices.peek() + val projModel: Matrix4f + get() = Matrix4f(projectionMatrix).mul(modelViewMatrix) @JvmStatic fun render3D(positionMatrix: Matrix4f, projMatrix: Matrix4f) { resetMatrices(positionMatrix) projectionMatrix.set(projMatrix) - setupGL { - val framebuffer = mc.framebuffer - val prevFramebuffer = (framebuffer.getColorAttachment() as GlTexture).getOrCreateFramebuffer( - (RenderSystem.getDevice() as GlBackend).bufferManager, - null - ) - - GlStateManager._glBindFramebuffer(GL_FRAMEBUFFER, prevFramebuffer) + // Render transient ESPs using the new pipeline + StaticESP.render(false) // Depth tested + DynamicESP.render(true) // Through walls - Treed.Static.render() - Treed.Dynamic.render() - - RenderEvent.Render.post() - } + RenderEvent.Render.post() } init { listen { - Treed.Static.clear() - Treed.Dynamic.clear() + StaticESP.clear() + DynamicESP.clear() RenderEvent.Upload.post() - Treed.Static.upload() - Treed.Dynamic.upload() + StaticESP.upload() + DynamicESP.upload() } } } diff --git a/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt b/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt index 77b830aca..c8e57ef5f 100644 --- a/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt +++ b/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt @@ -30,6 +30,7 @@ import org.lwjgl.opengl.GL30C.glDisable import org.lwjgl.opengl.GL30C.glEnable import org.lwjgl.opengl.GL30C.glLineWidth +// ToDo: Migrate particle system so we can remove this object GlStateUtils { private var depthTestState = true private var blendState = false diff --git a/src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt b/src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt new file mode 100644 index 000000000..479314688 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt @@ -0,0 +1,312 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.Lambda.mc +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.event.listener.SafeListener.Companion.listenConcurrently +import com.lambda.module.Module +import com.lambda.module.modules.client.StyleEditor +import com.lambda.threading.runSafe +import com.lambda.util.world.FastVector +import com.lambda.util.world.fastVectorOf +import com.mojang.blaze3d.buffers.GpuBuffer +import com.mojang.blaze3d.systems.RenderPass +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.VertexFormat +import net.minecraft.world.World +import net.minecraft.world.chunk.WorldChunk +import org.joml.Matrix4f +import org.joml.Quaternionf +import org.joml.Vector3f +import org.joml.Vector4f +import java.util.* +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentLinkedDeque + +/** + * Region-based chunked ESP system using MC 1.21.11's new render pipeline. + * + * This system: + * - Uses region-relative coordinates for precision-safe rendering + * - Uses MC's RenderPass and BufferBuilder system + * - Maintains per-chunk geometry for efficient updates + * - Supports both depth-tested and through-wall rendering + * + * @param owner The module that owns this ESP system + * @param throughWalls Whether to render through walls + * @param update The update function called for each block position + */ +class ChunkedRegionESP private constructor( + owner: Module, + private val throughWalls: () -> Boolean, + private val update: RegionShapeBuilder.(World, FastVector) -> Unit +) { + private val chunkMap = ConcurrentHashMap() + + private val WorldChunk.regionChunk + get() = chunkMap.getOrPut(pos.toLong()) { RegionChunk(this, this@ChunkedRegionESP) } + + private val uploadQueue = ConcurrentLinkedDeque<() -> Unit>() + private val rebuildQueue = ConcurrentLinkedDeque() + + /** Mark all tracked chunks for rebuild. */ + fun rebuild() { + rebuildQueue.clear() + rebuildQueue.addAll(chunkMap.values) + } + + /** + * Load all currently loaded world chunks and mark them for rebuild. Call this when the module + * is enabled to populate initial chunks. + */ + fun rebuildAll() { + runSafe { + val chunksArray = world.chunkManager.chunks.chunks + (0 until chunksArray.length()).forEach { i -> + chunksArray.get(i)?.regionChunk?.markDirty() + } + } + } + + /** Clear all chunk data. */ + fun clear() { + chunkMap.values.forEach { it.close() } + chunkMap.clear() + rebuildQueue.clear() + uploadQueue.clear() + } + + init { + owner.listen { event -> + val pos = event.pos + world.getWorldChunk(pos)?.regionChunk?.markDirty() + + val xInChunk = pos.x and 15 + val zInChunk = pos.z and 15 + + if (xInChunk == 0) world.getWorldChunk(pos.west())?.regionChunk?.markDirty() + if (xInChunk == 15) world.getWorldChunk(pos.east())?.regionChunk?.markDirty() + if (zInChunk == 0) world.getWorldChunk(pos.north())?.regionChunk?.markDirty() + if (zInChunk == 15) world.getWorldChunk(pos.south())?.regionChunk?.markDirty() + } + + owner.listen { event -> event.chunk.regionChunk.markDirty() } + + owner.listen { + val pos = it.chunk.pos.toLong() + chunkMap.remove(pos)?.close() + } + + owner.listenConcurrently { + val queueSize = rebuildQueue.size + val polls = minOf(StyleEditor.rebuildsPerTick, queueSize) + repeat(polls) { + rebuildQueue.poll()?.rebuild() + } + } + + owner.listen { + val polls = minOf(StyleEditor.uploadsPerTick, uploadQueue.size) + repeat(polls) { + uploadQueue.poll()?.invoke() + } + } + + owner.listen { render() } + } + + /** Render all chunks with geometry. */ + private fun render() { + val camera = mc.gameRenderer?.camera ?: return + val cameraPos = camera.pos + val framebuffer = mc.framebuffer ?: return + + val chunksWithData = chunkMap.values.filter { it.hasData } + if (chunksWithData.isEmpty()) return + + val chunkTransforms = + chunksWithData.map { chunk -> + val offset = chunk.region.computeCameraRelativeOffset(cameraPos) + val rotation = camera.rotation.conjugate(Quaternionf()) + val modelView = Matrix4f().rotation(rotation).translate(offset) + val transforms = RenderSystem.getDynamicUniforms().write( + modelView, + Vector4f(1.0f, 1.0f, 1.0f, 1.0f), // color modulator + Vector3f( + 0f, + 0f, + 0f + ), // model offset - ignored by vanilla shaders! + Matrix4f() // texture matrix + ) + chunk to transforms + } + + RenderSystem.getDevice() + .createCommandEncoder() + .createRenderPass( + { "Lambda ESP Faces" }, + framebuffer.colorAttachmentView, + OptionalInt.empty(), + framebuffer.depthAttachmentView, + OptionalDouble.empty() + ) + .use { renderPass -> + val pipeline = + if (throughWalls()) LambdaRenderPipelines.ESP_QUADS_THROUGH + else LambdaRenderPipelines.ESP_QUADS + renderPass.setPipeline(pipeline) + RenderSystem.bindDefaultUniforms(renderPass) + + chunkTransforms.forEach { (chunk, transforms) -> + renderPass.setUniform("DynamicTransforms", transforms) + chunk.renderFaces(renderPass) + } + } + + RenderSystem.getDevice() + .createCommandEncoder() + .createRenderPass( + { "Lambda ESP Edges" }, + framebuffer.colorAttachmentView, + OptionalInt.empty(), + framebuffer.depthAttachmentView, + OptionalDouble.empty() + ) + .use { renderPass -> + val pipeline = + if (throughWalls()) LambdaRenderPipelines.ESP_LINES_THROUGH + else LambdaRenderPipelines.ESP_LINES + renderPass.setPipeline(pipeline) + RenderSystem.bindDefaultUniforms(renderPass) + + chunkTransforms.forEach { (chunk, transforms) -> + renderPass.setUniform("DynamicTransforms", transforms) + chunk.renderEdges(renderPass) + } + } + } + + companion object { + /** + * Create a new chunked region ESP for a module. + * + * @param throughWalls Whether to render through walls + * @param update The update function called for each block position + */ + fun Module.newChunkedRegionESP( + throughWalls: () -> Boolean = { false }, + update: RegionShapeBuilder.(World, FastVector) -> Unit + ) = ChunkedRegionESP(this@newChunkedRegionESP, throughWalls, update) + } + + /** Per-chunk rendering data. */ + private inner class RegionChunk(val chunk: WorldChunk, val owner: ChunkedRegionESP) { + val region = RenderRegion.forChunk(chunk.pos.x, chunk.pos.z, chunk.bottomY) + + private var faceBuffer: GpuBuffer? = null + private var edgeBuffer: GpuBuffer? = null + private var faceIndexCount = 0 + private var edgeIndexCount = 0 + + var hasData = false + private set + + private var isDirty = false + + fun markDirty() { + isDirty = true + if (!owner.rebuildQueue.contains(this)) { + owner.rebuildQueue.add(this) + } + } + + /** + * Rebuild this chunk's geometry. Runs on a background thread - collects vertices into + * thread-safe collections. + */ + fun rebuild() { + if (!isDirty) return + val builder = RegionShapeBuilder(region) + + var blockCount = 0 + for (x in chunk.pos.startX..chunk.pos.endX) { + for (z in chunk.pos.startZ..chunk.pos.endZ) { + for (y in chunk.bottomY..chunk.height) { + owner.update(builder, chunk.world, fastVectorOf(x, y, z)) + blockCount++ + } + } + } + + owner.uploadQueue.add { upload(builder.collector) } + } + + /** Upload collected vertices to GPU. Must run on the main/render thread. */ + private fun upload(collector: RegionVertexCollector) { + faceBuffer?.close() + edgeBuffer?.close() + val result = collector.upload() + + faceBuffer = result.faces?.buffer + faceIndexCount = result.faces?.indexCount ?: 0 + + edgeBuffer = result.edges?.buffer + edgeIndexCount = result.edges?.indexCount ?: 0 + + hasData = faceBuffer != null || edgeBuffer != null + isDirty = false + } + + fun renderFaces(renderPass: RenderPass) { + val buffer = faceBuffer ?: return + if (faceIndexCount == 0) return + + renderPass.setVertexBuffer(0, buffer) + val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS) + val indexBuffer = shapeIndexBuffer.getIndexBuffer(faceIndexCount) + + renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) + renderPass.drawIndexed(0, 0, faceIndexCount, 1) + } + + fun renderEdges(renderPass: RenderPass) { + val buffer = edgeBuffer ?: return + if (edgeIndexCount == 0) return + + renderPass.setVertexBuffer(0, buffer) + val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.LINES) + val indexBuffer = shapeIndexBuffer.getIndexBuffer(edgeIndexCount) + + renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) + renderPass.drawIndexed(0, 0, edgeIndexCount, 1) + } + + fun close() { + faceBuffer?.close() + edgeBuffer?.close() + faceBuffer = null + edgeBuffer = null + hasData = false + } + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt new file mode 100644 index 000000000..3dcb4472c --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt @@ -0,0 +1,164 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.core.Loadable +import com.mojang.blaze3d.pipeline.BlendFunction +import com.mojang.blaze3d.pipeline.RenderPipeline +import com.mojang.blaze3d.platform.DepthTestFunction +import com.mojang.blaze3d.vertex.VertexFormat +import net.minecraft.client.gl.RenderPipelines +import net.minecraft.client.render.VertexFormats +import net.minecraft.util.Identifier + +object LambdaRenderPipelines : Loadable { + override val priority: Int + get() = 100 // High priority to ensure pipelines are ready early + + /** + * Base snippet for Lambda ESP rendering. Includes transforms, projection, and a custom + * per-region uniform. + */ + private val LAMBDA_ESP_SNIPPET = + RenderPipeline.builder(RenderPipelines.TRANSFORMS_AND_PROJECTION_SNIPPET).buildSnippet() + + /** + * Pipeline for static ESP faces (filled quads). + * - Translucent blending for see-through effect + * - No depth write to allow overlapping + * - No culling to see all faces + * - Uses TRIANGLES mode for maximum flexibility + */ + val ESP_FACES: RenderPipeline = + RenderPipelines.register( + RenderPipeline.builder(LAMBDA_ESP_SNIPPET) + .withLocation(Identifier.of("lambda", "pipeline/esp_faces")) + .withVertexShader(Identifier.ofVanilla("core/position_color")) + .withFragmentShader(Identifier.ofVanilla("core/position_color")) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) + .withCull(false) + .withVertexFormat( + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.TRIANGLES // ToDo: Should we use Triangles or Quads? + ) + .build() + ) + + /** + * Pipeline for static ESP faces that render through walls. + * - Same as ESP_FACES but with no depth test + */ + val ESP_FACES_THROUGH: RenderPipeline = + RenderPipelines.register( + RenderPipeline.builder(LAMBDA_ESP_SNIPPET) + .withLocation(Identifier.of("lambda", "pipeline/esp_faces_through")) + .withVertexShader(Identifier.ofVanilla("core/position_color")) + .withFragmentShader(Identifier.ofVanilla("core/position_color")) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withCull(false) + .withVertexFormat( + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.TRIANGLES + ) + .build() + ) + + /** + * Pipeline for ESP lines/outlines. + * - Uses MC's line rendering with per-vertex line width + * - No depth write for overlapping + * - No culling + */ + val ESP_LINES: RenderPipeline = + RenderPipelines.register( + RenderPipeline.builder(LAMBDA_ESP_SNIPPET, RenderPipelines.GLOBALS_SNIPPET) + .withLocation(Identifier.of("lambda", "pipeline/esp_lines")) + .withVertexShader(Identifier.ofVanilla("core/rendertype_lines")) + .withFragmentShader(Identifier.ofVanilla("core/rendertype_lines")) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) + .withCull(false) + .withVertexFormat( + VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, + VertexFormat.DrawMode.LINES + ) + .build() + ) + + /** Pipeline for ESP lines that render through walls. */ + val ESP_LINES_THROUGH: RenderPipeline = + RenderPipelines.register( + RenderPipeline.builder(LAMBDA_ESP_SNIPPET, RenderPipelines.GLOBALS_SNIPPET) + .withLocation(Identifier.of("lambda", "pipeline/esp_lines_through")) + .withVertexShader(Identifier.ofVanilla("core/rendertype_lines")) + .withFragmentShader(Identifier.ofVanilla("core/rendertype_lines")) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withCull(false) + .withVertexFormat( + VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, + VertexFormat.DrawMode.LINES + ) + .build() + ) + + /** + * Pipeline for quad-based ESP (compatible with existing shape building). Uses QUADS draw mode + * which MC converts to triangles internally. + */ + val ESP_QUADS: RenderPipeline = + RenderPipelines.register( + RenderPipeline.builder(LAMBDA_ESP_SNIPPET) + .withLocation(Identifier.of("lambda", "pipeline/esp_quads")) + .withVertexShader(Identifier.ofVanilla("core/position_color")) + .withFragmentShader(Identifier.ofVanilla("core/position_color")) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) + .withCull(false) + .withVertexFormat( + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.QUADS + ) + .build() + ) + + /** Pipeline for quad-based ESP that renders through walls. */ + val ESP_QUADS_THROUGH: RenderPipeline = + RenderPipelines.register( + RenderPipeline.builder(LAMBDA_ESP_SNIPPET) + .withLocation(Identifier.of("lambda", "pipeline/esp_quads_through")) + .withVertexShader(Identifier.ofVanilla("core/position_color")) + .withFragmentShader(Identifier.ofVanilla("core/position_color")) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withCull(false) + .withVertexFormat( + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.QUADS + ) + .build() + ) +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt new file mode 100644 index 000000000..fa8e96e87 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.Lambda.mc +import com.mojang.blaze3d.buffers.GpuBuffer +import com.mojang.blaze3d.systems.RenderPass +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.VertexFormat +import java.util.* + +/** + * Region-based renderer for ESP rendering using MC 1.21.11's new render pipeline. + * + * This renderer manages the lifecycle of dedicated GPU buffers for a specific region and provides + * methods to render them within a RenderPass. + * + * @param region The render region this renderer is associated with + */ +class RegionRenderer(val region: RenderRegion) { + + // Dedicated GPU buffers for faces and edges + private var faceVertexBuffer: GpuBuffer? = null + private var edgeVertexBuffer: GpuBuffer? = null + + // Index counts for draw calls + private var faceIndexCount = 0 + private var edgeIndexCount = 0 + + // State tracking + private var hasData = false + + /** + * Upload collected vertices from an external collector. This must be called on the main/render + * thread. + * + * @param collector The collector containing the geometry to upload + */ + fun upload(collector: RegionVertexCollector) { + val result = collector.upload() + + // Cleanup old buffers + faceVertexBuffer?.close() + edgeVertexBuffer?.close() + + // Assign new buffers and counts + faceVertexBuffer = result.faces?.buffer + faceIndexCount = result.faces?.indexCount ?: 0 + + edgeVertexBuffer = result.edges?.buffer + edgeIndexCount = result.edges?.indexCount ?: 0 + + hasData = faceVertexBuffer != null || edgeVertexBuffer != null + } + + /** + * Render faces using the given render pass. + * + * @param renderPass The active RenderPass to record commands into + */ + fun renderFaces(renderPass: RenderPass) { + val vb = faceVertexBuffer ?: return + if (faceIndexCount == 0) return + + renderPass.setVertexBuffer(0, vb) + // Use vanilla's sequential index buffer for quads + val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS) + val indexBuffer = shapeIndexBuffer.getIndexBuffer(faceIndexCount) + + renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) + renderPass.drawIndexed(0, 0, faceIndexCount, 1) + } + + /** + * Render edges using the given render pass. + * + * @param renderPass The active RenderPass to record commands into + */ + fun renderEdges(renderPass: RenderPass) { + val vb = edgeVertexBuffer ?: return + if (edgeIndexCount == 0) return + + renderPass.setVertexBuffer(0, vb) + // Use vanilla's sequential index buffer for lines + val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.LINES) + val indexBuffer = shapeIndexBuffer.getIndexBuffer(edgeIndexCount) + + renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) + renderPass.drawIndexed(0, 0, edgeIndexCount, 1) + } + + /** Clear all geometry data and release GPU resources. */ + fun clearData() { + faceVertexBuffer?.close() + edgeVertexBuffer?.close() + faceVertexBuffer = null + edgeVertexBuffer = null + faceIndexCount = 0 + edgeIndexCount = 0 + hasData = false + } + + /** Check if this renderer has any data to render. */ + fun hasData(): Boolean = hasData + + /** Clean up all resources. */ + fun close() { + clearData() + } + + companion object { + /** Helper to create a render pass targeting the main framebuffer. */ + fun createRenderPass(label: String): RenderPass? { + val framebuffer = mc.framebuffer ?: return null + + return RenderSystem.getDevice() + .createCommandEncoder() + .createRenderPass( + { label }, + framebuffer.colorAttachmentView, + OptionalInt.empty(), + framebuffer.depthAttachmentView, + OptionalDouble.empty() + ) + } + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt new file mode 100644 index 000000000..fcd713396 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt @@ -0,0 +1,338 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection +import com.lambda.graphics.renderer.esp.DynamicAABB +import com.lambda.module.modules.client.StyleEditor +import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.extension.outlineShape +import net.minecraft.block.BlockState +import net.minecraft.block.entity.BlockEntity +import net.minecraft.entity.Entity +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.shape.VoxelShape +import java.awt.Color + +/** + * Shape builder for region-based rendering. All coordinates are automatically converted to + * region-relative positions. + * + * This class provides the same DSL as ShapeDsl but collects vertex data in thread-safe collections + * for later upload to MC's BufferBuilder. + * + * @param region The render region (provides origin for coordinate conversion) + */ +class RegionShapeBuilder(val region: RenderRegion) { + val collector = RegionVertexCollector() + + private val lineWidth: Float + get() = StyleEditor.outlineWidth.toFloat() + + /** Convert world coordinates to region-relative. */ + private fun toRelative(x: Double, y: Double, z: Double) = Triple( + (x - region.originX).toFloat(), + (y - region.originY).toFloat(), + (z - region.originZ).toFloat() + ) + + /** Add a colored quad face (filled rectangle). */ + fun filled( + box: Box, + bottomColor: Color, + topColor: Color = bottomColor, + sides: Int = DirectionMask.ALL + ) { + val (x1, y1, z1) = toRelative(box.minX, box.minY, box.minZ) + val (x2, y2, z2) = toRelative(box.maxX, box.maxY, box.maxZ) + + // Bottom-left-back, bottom-left-front, etc. + if (sides.hasDirection(DirectionMask.EAST)) { + // East face (+X) + faceVertex(x2, y1, z1, bottomColor) + faceVertex(x2, y2, z1, topColor) + faceVertex(x2, y2, z2, topColor) + faceVertex(x2, y1, z2, bottomColor) + } + if (sides.hasDirection(DirectionMask.WEST)) { + // West face (-X) + faceVertex(x1, y1, z1, bottomColor) + faceVertex(x1, y1, z2, bottomColor) + faceVertex(x1, y2, z2, topColor) + faceVertex(x1, y2, z1, topColor) + } + if (sides.hasDirection(DirectionMask.UP)) { + // Top face (+Y) + faceVertex(x1, y2, z1, topColor) + faceVertex(x1, y2, z2, topColor) + faceVertex(x2, y2, z2, topColor) + faceVertex(x2, y2, z1, topColor) + } + if (sides.hasDirection(DirectionMask.DOWN)) { + // Bottom face (-Y) + faceVertex(x1, y1, z1, bottomColor) + faceVertex(x2, y1, z1, bottomColor) + faceVertex(x2, y1, z2, bottomColor) + faceVertex(x1, y1, z2, bottomColor) + } + if (sides.hasDirection(DirectionMask.SOUTH)) { + // South face (+Z) + faceVertex(x1, y1, z2, bottomColor) + faceVertex(x2, y1, z2, bottomColor) + faceVertex(x2, y2, z2, topColor) + faceVertex(x1, y2, z2, topColor) + } + if (sides.hasDirection(DirectionMask.NORTH)) { + // North face (-Z) + faceVertex(x1, y1, z1, bottomColor) + faceVertex(x1, y2, z1, topColor) + faceVertex(x2, y2, z1, topColor) + faceVertex(x2, y1, z1, bottomColor) + } + } + + fun filled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = + filled(box, color, color, sides) + + fun filled(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { + box.pair?.second?.let { filled(it, color, sides) } + } + + fun filled(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) = + runSafe { + val shape = outlineShape(state, pos) + if (shape.isEmpty) { + filled(Box(pos), color, sides) + } else { + filled(shape, color, sides) + } + } + + fun filled(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) = runSafe { + filled(pos, blockState(pos), color, sides) + } + + fun filled(pos: BlockPos, entity: BlockEntity, color: Color, sides: Int = DirectionMask.ALL) = + filled(pos, entity.cachedState, color, sides) + + fun filled(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) { + shape.boundingBoxes.forEach { filled(it, color, color, sides) } + } + + /** Add outline (lines) for a box. */ + fun outline( + box: Box, + bottomColor: Color, + topColor: Color = bottomColor, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + val (x1, y1, z1) = toRelative(box.minX, box.minY, box.minZ) + val (x2, y2, z2) = toRelative(box.maxX, box.maxY, box.maxZ) + + val hasEast = sides.hasDirection(DirectionMask.EAST) + val hasWest = sides.hasDirection(DirectionMask.WEST) + val hasUp = sides.hasDirection(DirectionMask.UP) + val hasDown = sides.hasDirection(DirectionMask.DOWN) + val hasSouth = sides.hasDirection(DirectionMask.SOUTH) + val hasNorth = sides.hasDirection(DirectionMask.NORTH) + + // Top edges + if (mode.check(hasUp, hasNorth)) line(x1, y2, z1, x2, y2, z1, topColor) + if (mode.check(hasUp, hasSouth)) line(x1, y2, z2, x2, y2, z2, topColor) + if (mode.check(hasUp, hasWest)) line(x1, y2, z1, x1, y2, z2, topColor) + if (mode.check(hasUp, hasEast)) line(x2, y2, z2, x2, y2, z1, topColor) + + // Bottom edges + if (mode.check(hasDown, hasNorth)) line(x1, y1, z1, x2, y1, z1, bottomColor) + if (mode.check(hasDown, hasSouth)) line(x1, y1, z2, x2, y1, z2, bottomColor) + if (mode.check(hasDown, hasWest)) line(x1, y1, z1, x1, y1, z2, bottomColor) + if (mode.check(hasDown, hasEast)) line(x2, y1, z1, x2, y1, z2, bottomColor) + + // Vertical edges + if (mode.check(hasWest, hasNorth)) line(x1, y2, z1, x1, y1, z1, topColor, bottomColor) + if (mode.check(hasNorth, hasEast)) line(x2, y2, z1, x2, y1, z1, topColor, bottomColor) + if (mode.check(hasEast, hasSouth)) line(x2, y2, z2, x2, y1, z2, topColor, bottomColor) + if (mode.check(hasSouth, hasWest)) line(x1, y2, z2, x1, y1, z2, topColor, bottomColor) + } + + fun outline( + box: Box, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = outline(box, color, color, sides, mode) + + fun outline( + box: DynamicAABB, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + box.pair?.second?.let { outline(it, color, sides, mode) } + } + + fun outline( + pos: BlockPos, + state: BlockState, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = runSafe { + val shape = outlineShape(state, pos) + if (shape.isEmpty) { + outline(Box(pos), color, sides, mode) + } else { + outline(shape, color, sides, mode) + } + } + + fun outline( + pos: BlockPos, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = runSafe { outline(pos, blockState(pos), color, sides, mode) } + + fun outline( + pos: BlockPos, + entity: BlockEntity, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = runSafe { outline(pos, entity.cachedState, color, sides, mode) } + + fun outline( + shape: VoxelShape, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + shape.boundingBoxes.forEach { outline(it, color, sides, mode) } + } + + /** Add both filled and outline for a box. */ + fun box( + pos: BlockPos, + state: BlockState, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = runSafe { + filled(pos, state, filledColor, sides) + outline(pos, state, outlineColor, sides, mode) + } + + fun box( + pos: BlockPos, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = runSafe { + filled(pos, filledColor, sides) + outline(pos, outlineColor, sides, mode) + } + + fun box( + box: Box, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + filled(box, filledColor, sides) + outline(box, outlineColor, sides, mode) + } + + fun box( + box: DynamicAABB, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + filled(box, filledColor, sides) + outline(box, outlineColor, sides, mode) + } + + fun box( + entity: BlockEntity, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = runSafe { + filled(entity.pos, entity, color, sides) + outline(entity.pos, entity, color, sides, mode) + } + + fun box( + entity: Entity, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = runSafe { + filled(entity.boundingBox, color, sides) + outline(entity.boundingBox, color, sides, mode) + } + + // =========== Private helpers =========== + + private fun faceVertex(x: Float, y: Float, z: Float, color: Color) { + collector.addFaceVertex(x, y, z, color) + } + + private fun line( + x1: Float, + y1: Float, + z1: Float, + x2: Float, + y2: Float, + z2: Float, + color: Color + ) { + line(x1, y1, z1, x2, y2, z2, color, color) + } + + private fun line( + x1: Float, + y1: Float, + z1: Float, + x2: Float, + y2: Float, + z2: Float, + color1: Color, + color2: Color + ) { + // Calculate normal (direction of line) + val dx = x2 - x1 + val dy = y2 - y1 + val dz = z2 - z1 + val len = kotlin.math.sqrt(dx * dx + dy * dy + dz * dz) + val nx = if (len > 0) dx / len else 0f + val ny = if (len > 0) dy / len else 1f + val nz = if (len > 0) dz / len else 0f + + collector.addEdgeVertex(x1, y1, z1, color1, nx, ny, nz, lineWidth) + collector.addEdgeVertex(x2, y2, z2, color2, nx, ny, nz, lineWidth) + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt new file mode 100644 index 000000000..e322816ab --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt @@ -0,0 +1,168 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.mojang.blaze3d.buffers.GpuBuffer +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.VertexFormat +import net.minecraft.client.render.BufferBuilder +import net.minecraft.client.render.VertexFormats +import net.minecraft.client.util.BufferAllocator +import java.awt.Color +import java.util.concurrent.ConcurrentLinkedDeque + +/** + * Thread-safe vertex collector for region-based rendering. + * + * Collects vertex data on background threads using thread-safe collections, then writes to MC's + * BufferBuilder and uploads on the main thread. + */ +class RegionVertexCollector { + val faceVertices = ConcurrentLinkedDeque() + val edgeVertices = ConcurrentLinkedDeque() + + /** Face vertex data (position + color). */ + data class FaceVertex( + val x: Float, + val y: Float, + val z: Float, + val r: Int, + val g: Int, + val b: Int, + val a: Int + ) + + /** Edge vertex data (position + color + normal + line width). */ + data class EdgeVertex( + val x: Float, + val y: Float, + val z: Float, + val r: Int, + val g: Int, + val b: Int, + val a: Int, + val nx: Float, + val ny: Float, + val nz: Float, + val lineWidth: Float + ) + + /** Add a face vertex. */ + fun addFaceVertex(x: Float, y: Float, z: Float, color: Color) { + faceVertices.add(FaceVertex(x, y, z, color.red, color.green, color.blue, color.alpha)) + } + + /** Add an edge vertex. */ + fun addEdgeVertex( + x: Float, + y: Float, + z: Float, + color: Color, + nx: Float, + ny: Float, + nz: Float, + lineWidth: Float + ) { + edgeVertices.add( + EdgeVertex(x, y, z, color.red, color.green, color.blue, color.alpha, nx, ny, nz, lineWidth) + ) + } + + /** + * Upload collected data to GPU buffers. Must be called on the main/render thread. + * + * @return Pair of (faceBuffer, edgeBuffer) and their index counts, or null if no data + */ + fun upload(): UploadResult { + val faceResult = + if (faceVertices.isNotEmpty()) { + uploadFaces() + } else null + + val edgeResult = + if (edgeVertices.isNotEmpty()) { + uploadEdges() + } else null + + return UploadResult(faceResult, edgeResult) + } + + private fun uploadFaces(): BufferResult { + // 16 bytes per vertex (3 floats + 4 bytes color) + BufferAllocator(faceVertices.size * 16).use { allocator -> + val builder = + BufferBuilder( + allocator, + VertexFormat.DrawMode.QUADS, + VertexFormats.POSITION_COLOR + ) + + faceVertices.forEach { v -> + builder.vertex(v.x, v.y, v.z).color(v.r, v.g, v.b, v.a) + } + + builder.endNullable()?.let { built -> + val gpuDevice = RenderSystem.getDevice() + val buffer = gpuDevice.createBuffer( + { "Lambda ESP Face Buffer" }, + GpuBuffer.USAGE_VERTEX, + built.buffer + ) + val indexCount = built.drawParameters.indexCount() + built.close() + return BufferResult(buffer, indexCount) + } + } + return BufferResult(null, 0) + } + + private fun uploadEdges(): BufferResult { + // 32 bytes per vertex + BufferAllocator(edgeVertices.size * 32).use { allocator -> + val builder = + BufferBuilder( + allocator, + VertexFormat.DrawMode.LINES, + VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH + ) + + edgeVertices.forEach { v -> + builder.vertex(v.x, v.y, v.z) + .color(v.r, v.g, v.b, v.a) + .normal(v.nx, v.ny, v.nz) + .lineWidth(v.lineWidth) + } + + builder.endNullable()?.let { built -> + val gpuDevice = RenderSystem.getDevice() + val buffer = gpuDevice.createBuffer( + { "Lambda ESP Edge Buffer" }, + GpuBuffer.USAGE_VERTEX, + built.buffer + ) + val indexCount = built.drawParameters.indexCount() + built.close() + return BufferResult(buffer, indexCount) + } + } + return BufferResult(null, 0) + } + + data class BufferResult(val buffer: GpuBuffer?, val indexCount: Int) + data class UploadResult(val faces: BufferResult?, val edges: BufferResult?) +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt b/src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt new file mode 100644 index 000000000..915c396a5 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import net.minecraft.util.math.Vec3d +import org.joml.Vector3f + +/** + * A render region represents a chunk-sized area in the world where vertices are stored relative to + * the region's origin. This solves floating-point precision issues at large world coordinates. + * + * @param originX The X coordinate of the region's origin (typically chunk corner) + * @param originY The Y coordinate of the region's origin + * @param originZ The Z coordinate of the region's origin + */ +class RenderRegion(val originX: Int, val originY: Int, val originZ: Int) { + /** + * Compute the camera-relative offset for this region. This is done in double precision to + * maintain accuracy at large coordinates. + * + * @param cameraPos The camera's world position (double precision) + * @return The offset from camera to region origin (small float, high precision) + */ + fun computeCameraRelativeOffset(cameraPos: Vec3d): Vector3f { + val offsetX = originX.toDouble() - cameraPos.x + val offsetY = originY.toDouble() - cameraPos.y + val offsetZ = originZ.toDouble() - cameraPos.z + return Vector3f(offsetX.toFloat(), offsetY.toFloat(), offsetZ.toFloat()) + } + + companion object { + /** Standard size of a render region (matches Minecraft chunk size). */ + const val REGION_SIZE = 16 + + /** + * Create a region for a chunk position. + * + * @param chunkX Chunk X coordinate + * @param chunkZ Chunk Z coordinate + * @param bottomY World bottom Y coordinate (typically -64) + */ + fun forChunk(chunkX: Int, chunkZ: Int, bottomY: Int) = + RenderRegion(chunkX * 16, bottomY, chunkZ * 16) + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt b/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt new file mode 100644 index 000000000..c5d62fb23 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.Lambda.mc +import com.mojang.blaze3d.systems.RenderSystem +import org.joml.Matrix4f +import org.joml.Quaternionf +import org.joml.Vector3f +import org.joml.Vector4f +import java.util.concurrent.ConcurrentHashMap +import kotlin.math.floor + +/** + * Modern replacement for the legacy Treed system. Handles geometry that is cleared and rebuilt + * every tick. Uses region-based rendering for precision. + */ +class TransientRegionESP(val name: String) { + private val renderers = ConcurrentHashMap() + private val builders = ConcurrentHashMap() + + /** Get or create a builder for a specific region. */ + fun getBuilder(x: Double, y: Double, z: Double): RegionShapeBuilder { + val size = RenderRegion.REGION_SIZE + val rx = (size * floor(x / size)).toInt() + val ry = (size * floor(y / size)).toInt() + val rz = (size * floor(z / size)).toInt() + + val key = (rx.toLong() and 0xFFFFFFFFL) or ((rz.toLong() and 0xFFFFFFFFL) shl 32) + + return builders.getOrPut(key) { RegionShapeBuilder(RenderRegion(rx, ry, rz)) } + } + + /** Clear all current builders. Call this at the end of every tick. */ + fun clear() { + builders.clear() + } + + /** Upload collected geometry to GPU. Must be called on main thread. */ + fun upload() { + val activeKeys = builders.keys().asSequence().toSet() + + builders.forEach { (key, builder) -> + val renderer = renderers.getOrPut(key) { RegionRenderer(builder.region) } + renderer.upload(builder.collector) + } + + renderers.forEach { (key, renderer) -> + if (key !in activeKeys) { + renderer.clearData() + } + } + } + + /** Render all active regions. */ + fun render(throughWalls: Boolean = false) { + val camera = mc.gameRenderer?.camera ?: return + val cameraPos = camera.pos + + val activeRenderers = renderers.values.filter { it.hasData() } + if (activeRenderers.isEmpty()) return + + val transforms = + activeRenderers.map { renderer -> + val offset = renderer.region.computeCameraRelativeOffset(cameraPos) + val rotation = camera.rotation.conjugate(Quaternionf()) + val modelView = Matrix4f().rotation(rotation).translate(offset) + + val dynamicTransform = + RenderSystem.getDynamicUniforms() + .write( + modelView, + Vector4f(1f, 1f, 1f, 1f), + Vector3f(0f, 0f, 0f), + Matrix4f() + ) + renderer to dynamicTransform + } + + val facePass = RegionRenderer.createRenderPass("Transient ESP Faces ($name)") ?: return + facePass.use { pass -> + val pipeline = + if (throughWalls) LambdaRenderPipelines.ESP_QUADS_THROUGH + else LambdaRenderPipelines.ESP_QUADS + pass.setPipeline(pipeline) + RenderSystem.bindDefaultUniforms(pass) + transforms.forEach { (renderer, transform) -> + pass.setUniform("DynamicTransforms", transform) + renderer.renderFaces(pass) + } + } + + val edgePass = RegionRenderer.createRenderPass("Transient ESP Edges ($name)") ?: return + edgePass.use { pass -> + val pipeline = + if (throughWalls) LambdaRenderPipelines.ESP_LINES_THROUGH + else LambdaRenderPipelines.ESP_LINES + pass.setPipeline(pipeline) + RenderSystem.bindDefaultUniforms(pass) + transforms.forEach { (renderer, transform) -> + pass.setUniform("DynamicTransforms", transform) + renderer.renderEdges(pass) + } + } + } + + fun close() { + renderers.values.forEach { it.close() } + renderers.clear() + builders.clear() + } +} diff --git a/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt deleted file mode 100644 index 03cf28c85..000000000 --- a/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.renderer.esp - -import com.lambda.event.events.RenderEvent -import com.lambda.event.events.TickEvent -import com.lambda.event.events.WorldEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.event.listener.SafeListener.Companion.listenConcurrently -import com.lambda.module.Module -import com.lambda.module.modules.client.StyleEditor -import com.lambda.threading.awaitMainThread -import com.lambda.util.world.FastVector -import com.lambda.util.world.fastVectorOf -import net.minecraft.world.World -import net.minecraft.world.chunk.WorldChunk -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedDeque - -class ChunkedESP private constructor( - owner: Module, - private val update: ShapeBuilder.(World, FastVector) -> Unit -) { - private val rendererMap = ConcurrentHashMap() - private val WorldChunk.renderer - get() = rendererMap.getOrPut(pos.toLong()) { EspChunk(this, this@ChunkedESP) } - - private val uploadQueue = ConcurrentLinkedDeque<() -> Unit>() - private val rebuildQueue = ConcurrentLinkedDeque() - - private var ticks = 0 - - fun rebuild() { - rebuildQueue.clear() - rebuildQueue.addAll(rendererMap.values) - } - - init { - listen { event -> - world.getWorldChunk(event.pos).renderer.notifyChunks() - } - - listen { event -> - event.chunk.renderer.notifyChunks() - } - - listen { - val pos = it.chunk.pos.toLong() - rendererMap.remove(pos)?.notifyChunks() - } - - owner.listenConcurrently { - val polls = minOf(StyleEditor.rebuildsPerTick, rebuildQueue.size) - repeat(polls) { rebuildQueue.poll()?.rebuild() } - } - - owner.listen { - val polls = minOf(StyleEditor.uploadsPerTick, uploadQueue.size) - repeat(polls) { uploadQueue.poll()?.invoke() } - } - - owner.listen { - rendererMap.values.forEach { it.renderer.render() } - } - } - - companion object { - fun Module.newChunkedESP( - update: ShapeBuilder.(World, FastVector) -> Unit - ) = ChunkedESP(this@newChunkedESP, update) - } - - private class EspChunk(val chunk: WorldChunk, val owner: ChunkedESP) { - var renderer = Treed(static = true) - private val builder: ShapeBuilder - get() = ShapeBuilder(renderer.faceBuilder, renderer.edgeBuilder) - - /*val neighbors = listOf(1 to 0, 0 to 1, -1 to 0, 0 to -1) - .map { ChunkPos(chunk.pos.x + it.first, chunk.pos.z + it.second) }*/ - - fun notifyChunks() { - owner.rendererMap[chunk.pos.toLong()]?.let { - if (!owner.rebuildQueue.contains(it)) - owner.rebuildQueue.add(it) - } - } - - suspend fun rebuild() { - renderer.clear() - renderer = awaitMainThread { Treed(static = true) } - - for (x in chunk.pos.startX..chunk.pos.endX) - for (z in chunk.pos.startZ..chunk.pos.endZ) - for (y in chunk.bottomY..chunk.height) - owner.update(builder, chunk.world, fastVectorOf(x, y, z)) - - owner.uploadQueue.add { renderer.upload() } - } - } -} diff --git a/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt b/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt index eaf223370..ca1bd8c64 100644 --- a/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt +++ b/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt @@ -17,13 +17,7 @@ package com.lambda.graphics.renderer.esp -import com.lambda.graphics.pipeline.VertexBuilder -import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection -import com.lambda.threading.runSafe -import com.lambda.util.BlockUtils.blockState -import com.lambda.util.extension.max -import com.lambda.util.extension.min -import com.lambda.util.extension.outlineShape +import com.lambda.graphics.mc.TransientRegionESP import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.entity.Entity @@ -35,326 +29,206 @@ import java.awt.Color @DslMarker annotation class ShapeDsl -class ShapeBuilder( - val faces: VertexBuilder = VertexBuilder(), - val edges: VertexBuilder = VertexBuilder(), -) { - @ShapeDsl - fun filled( - box : DynamicAABB, - color : Color, - sides : Int = DirectionMask.ALL, - ) = faces.apply { - val boxes = box.pair ?: return@apply - - val pos11 = boxes.first.min - val pos12 = boxes.first.max - val pos21 = boxes.second.min - val pos22 = boxes.second.max - - val blb by lazy { vertex { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color) } } - val blf by lazy { vertex { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color) } } - val brb by lazy { vertex { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color) } } - val brf by lazy { vertex { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color) } } - val tlb by lazy { vertex { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color) } } - val tlf by lazy { vertex { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color) } } - val trb by lazy { vertex { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color) } } - val trf by lazy { vertex { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color) } } - - if (sides.hasDirection(DirectionMask.EAST)) buildQuad(brb, trb, trf, brf) - if (sides.hasDirection(DirectionMask.WEST)) buildQuad(blb, blf, tlf, tlb) - if (sides.hasDirection(DirectionMask.UP)) buildQuad(tlb, tlf, trf, trb) - if (sides.hasDirection(DirectionMask.DOWN)) buildQuad(blb, brb, brf, blf) - if (sides.hasDirection(DirectionMask.SOUTH)) buildQuad(blf, brf, trf, tlf) - if (sides.hasDirection(DirectionMask.NORTH)) buildQuad(blb, tlb, trb, brb) - } - - @ShapeDsl - fun filled( - box : Box, - bottomColor : Color, - topColor : Color = bottomColor, - sides : Int = DirectionMask.ALL - ) = faces.apply { - val pos1 = box.min - val pos2 = box.max - - val blb by lazy { vertex { vec3(pos1.x, pos1.y, pos1.z).color(bottomColor) } } - val blf by lazy { vertex { vec3(pos1.x, pos1.y, pos2.z).color(bottomColor) } } - val brb by lazy { vertex { vec3(pos2.x, pos1.y, pos1.z).color(bottomColor) } } - val brf by lazy { vertex { vec3(pos2.x, pos1.y, pos2.z).color(bottomColor) } } - - val tlb by lazy { vertex { vec3(pos1.x, pos2.y, pos1.z).color(topColor) } } - val tlf by lazy { vertex { vec3(pos1.x, pos2.y, pos2.z).color(topColor) } } - val trb by lazy { vertex { vec3(pos2.x, pos2.y, pos1.z).color(topColor) } } - val trf by lazy { vertex { vec3(pos2.x, pos2.y, pos2.z).color(topColor) } } - - if (sides.hasDirection(DirectionMask.EAST)) buildQuad(brb, trb, trf, brf) - if (sides.hasDirection(DirectionMask.WEST)) buildQuad(blb, blf, tlf, tlb) - if (sides.hasDirection(DirectionMask.UP)) buildQuad(tlb, tlf, trf, trb) - if (sides.hasDirection(DirectionMask.DOWN)) buildQuad(blb, brb, brf, blf) - if (sides.hasDirection(DirectionMask.SOUTH)) buildQuad(blf, brf, trf, tlf) - if (sides.hasDirection(DirectionMask.NORTH)) buildQuad(blb, tlb, trb, brb) - } - - @ShapeDsl - fun filled( - pos : BlockPos, - state : BlockState, - color : Color, - sides : Int = DirectionMask.ALL, - ) = runSafe { faces.apply { - val shape = outlineShape(state, pos) - if (shape.isEmpty) { - filled(Box(pos), color, sides) - } else { - filled(shape, color, sides) - } - } } - - @ShapeDsl - fun filled( - pos : BlockPos, - color : Color, - sides : Int = DirectionMask.ALL, - ) = runSafe { faces.apply { filled(pos, blockState(pos), color, sides) } } - - @ShapeDsl - fun filled( - pos : BlockPos, - entity : BlockEntity, - color : Color, - sides : Int = DirectionMask.ALL, - ) = filled(pos, entity.cachedState, color, sides) - - @ShapeDsl - fun filled( - shape: VoxelShape, - color: Color, - sides: Int = DirectionMask.ALL, - ) { - shape.boundingBoxes - .forEach { filled(it, color, color, sides) } - } - - @ShapeDsl - fun filled( - box : Box, - color : Color, - sides : Int = DirectionMask.ALL, - ) = filled(box, color, color, sides) - - @ShapeDsl - fun outline( - box : DynamicAABB, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = edges.apply { - val boxes = box.pair ?: return@apply - - val pos11 = boxes.first.min - val pos12 = boxes.first.max - val pos21 = boxes.second.min - val pos22 = boxes.second.max - - val blb by lazy { vertex { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color) } } - val blf by lazy { vertex { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color) } } - val brb by lazy { vertex { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color) } } - val brf by lazy { vertex { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color) } } - val tlb by lazy { vertex { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color) } } - val tlf by lazy { vertex { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color) } } - val trb by lazy { vertex { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color) } } - val trf by lazy { vertex { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color) } } - - val hasEast = sides.hasDirection(DirectionMask.EAST) - val hasWest = sides.hasDirection(DirectionMask.WEST) - val hasUp = sides.hasDirection(DirectionMask.UP) - val hasDown = sides.hasDirection(DirectionMask.DOWN) - val hasSouth = sides.hasDirection(DirectionMask.SOUTH) - val hasNorth = sides.hasDirection(DirectionMask.NORTH) - - if (mode.check(hasUp, hasNorth)) buildLine(tlb, trb) - if (mode.check(hasUp, hasSouth)) buildLine(tlf, trf) - if (mode.check(hasUp, hasWest)) buildLine(tlb, tlf) - if (mode.check(hasUp, hasEast)) buildLine(trf, trb) - - if (mode.check(hasDown, hasNorth)) buildLine(blb, brb) - if (mode.check(hasDown, hasSouth)) buildLine(blf, brf) - if (mode.check(hasDown, hasWest)) buildLine(blb, blf) - if (mode.check(hasDown, hasEast)) buildLine(brb, brf) - - if (mode.check(hasWest, hasNorth)) buildLine(tlb, blb) - if (mode.check(hasNorth, hasEast)) buildLine(trb, brb) - if (mode.check(hasEast, hasSouth)) buildLine(trf, brf) - if (mode.check(hasSouth, hasWest)) buildLine(tlf, blf) - } - - @ShapeDsl - fun outline( - box : Box, - bottomColor : Color, - topColor : Color = bottomColor, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = edges.apply { - val pos1 = box.min - val pos2 = box.max - - val blb by lazy { vertex { vec3(pos1.x, pos1.y, pos1.z).color(bottomColor) } } - val blf by lazy { vertex { vec3(pos1.x, pos1.y, pos2.z).color(bottomColor) } } - val brb by lazy { vertex { vec3(pos2.x, pos1.y, pos1.z).color(bottomColor) } } - val brf by lazy { vertex { vec3(pos2.x, pos1.y, pos2.z).color(bottomColor) } } - val tlb by lazy { vertex { vec3(pos1.x, pos2.y, pos1.z).color(topColor) } } - val tlf by lazy { vertex { vec3(pos1.x, pos2.y, pos2.z).color(topColor) } } - val trb by lazy { vertex { vec3(pos2.x, pos2.y, pos1.z).color(topColor) } } - val trf by lazy { vertex { vec3(pos2.x, pos2.y, pos2.z).color(topColor) } } - - val hasEast = sides.hasDirection(DirectionMask.EAST) - val hasWest = sides.hasDirection(DirectionMask.WEST) - val hasUp = sides.hasDirection(DirectionMask.UP) - val hasDown = sides.hasDirection(DirectionMask.DOWN) - val hasSouth = sides.hasDirection(DirectionMask.SOUTH) - val hasNorth = sides.hasDirection(DirectionMask.NORTH) - - if (mode.check(hasUp, hasNorth)) buildLine(tlb, trb) - if (mode.check(hasUp, hasSouth)) buildLine(tlf, trf) - if (mode.check(hasUp, hasWest)) buildLine(tlb, tlf) - if (mode.check(hasUp, hasEast)) buildLine(trf, trb) - - if (mode.check(hasDown, hasNorth)) buildLine(blb, brb) - if (mode.check(hasDown, hasSouth)) buildLine(blf, brf) - if (mode.check(hasDown, hasWest)) buildLine(blb, blf) - if (mode.check(hasDown, hasEast)) buildLine(brb, brf) - - if (mode.check(hasWest, hasNorth)) buildLine(tlb, blb) - if (mode.check(hasNorth, hasEast)) buildLine(trb, brb) - if (mode.check(hasEast, hasSouth)) buildLine(trf, brf) - if (mode.check(hasSouth, hasWest)) buildLine(tlf, blf) - } - - @ShapeDsl - fun outline( - pos : BlockPos, - state : BlockState, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = runSafe { - val shape = outlineShape(state, pos) - if (shape.isEmpty) { - outline(Box(pos), color, sides, mode) - } else { - outline(shape, color, sides, mode) - } - } - - @ShapeDsl - fun outline( - pos : BlockPos, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = runSafe { outline(pos, blockState(pos), color, sides, mode) } - - @ShapeDsl - fun outline( - pos : BlockPos, - entity : BlockEntity, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = runSafe { outline(pos, entity.cachedState, color, sides, mode) } - - @ShapeDsl - fun outline( - shape : VoxelShape, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) { - shape.boundingBoxes - .forEach { outline(it, color, sides, mode) } - } - - @ShapeDsl - fun outline( - box : Box, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) { - outline(box, color, color, sides, mode) - } - - @ShapeDsl - fun box( - pos : BlockPos, - state : BlockState, - filled : Color, - outline : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = runSafe { - filled(pos, state, filled, sides) - outline(pos, state, outline, sides, mode) - } - - @ShapeDsl - fun box( - pos : BlockPos, - filled : Color, - outline : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = runSafe { - filled(pos, filled, sides) - outline(pos, outline, sides, mode) - } - - @ShapeDsl - fun box( - box : DynamicAABB, - filled : Color, - outline : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) { - filled(box, filled, sides) - outline(box, outline, sides, mode) - } - - @ShapeDsl - fun box( - box : Box, - filled : Color, - outline : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) { - filled(box, filled, sides) - outline(box, outline, sides, mode) - } - - @ShapeDsl - fun box( - entity : BlockEntity, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = runSafe { - filled(entity.pos, entity, color, sides) - outline(entity.pos, entity, color, sides, mode) - } - - @ShapeDsl - fun box( - entity : Entity, - color : Color, - sides : Int = DirectionMask.ALL, - mode : DirectionMask.OutlineMode = DirectionMask.OutlineMode.Or, - ) = runSafe { - filled(entity.boundingBox, color, sides) - outline(entity.boundingBox, color, sides, mode) - } +/** + * Bridge class that provides the legacy ShapeBuilder API while writing to the new + * TransientRegionESP. + */ +class ShapeBuilder(val esp: TransientRegionESP) { + @ShapeDsl + fun filled( + box: Box, + bottomColor: Color, + topColor: Color = bottomColor, + sides: Int = DirectionMask.ALL + ) = esp.getBuilder(box.minX, box.minY, box.minZ).filled(box, bottomColor, topColor, sides) + + @ShapeDsl + fun filled(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) = + esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) + .filled(pos, state, color, sides) + + @ShapeDsl + fun filled(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) = + esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) + .filled(pos, color, sides) + + @ShapeDsl + fun filled( + pos: BlockPos, + entity: BlockEntity, + color: Color, + sides: Int = DirectionMask.ALL + ) = + esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) + .filled(pos, entity, color, sides) + + @ShapeDsl + fun filled(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) = + esp.getBuilder( + shape.boundingBoxes[0].minX, + shape.boundingBoxes[0].minY, + shape.boundingBoxes[0].minZ + ) + .filled(shape, color, sides) + + @ShapeDsl + fun filled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = + filled(box, color, color, sides) + + @ShapeDsl + fun outline( + box: Box, + bottomColor: Color, + topColor: Color = bottomColor, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) = + esp.getBuilder(box.minX, box.minY, box.minZ) + .outline(box, bottomColor, topColor, sides, mode) + + @ShapeDsl + fun outline( + pos: BlockPos, + state: BlockState, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) = + esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) + .outline(pos, state, color, sides, mode) + + @ShapeDsl + fun outline( + pos: BlockPos, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) = + esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) + .outline(pos, color, sides, mode) + + @ShapeDsl + fun outline( + pos: BlockPos, + entity: BlockEntity, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) = + esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) + .outline(pos, entity, color, sides, mode) + + @ShapeDsl + fun outline( + shape: VoxelShape, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) = + esp.getBuilder( + shape.boundingBoxes[0].minX, + shape.boundingBoxes[0].minY, + shape.boundingBoxes[0].minZ + ) + .outline(shape, color, sides, mode) + + @ShapeDsl + fun outline( + box: Box, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) = outline(box, color, color, sides, mode) + + @ShapeDsl + fun box( + pos: BlockPos, + state: BlockState, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) { + filled(pos, state, filled, sides) + outline(pos, state, outline, sides, mode) + } + + @ShapeDsl + fun box( + pos: BlockPos, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) { + filled(pos, filled, sides) + outline(pos, outline, sides, mode) + } + + @ShapeDsl + fun box( + box: Box, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) { + filled(box, filled, sides) + outline(box, outline, sides, mode) + } + + @ShapeDsl + fun box( + entity: BlockEntity, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) { + filled(entity.pos, entity, color, sides) + outline(entity.pos, entity, color, sides, mode) + } + + @ShapeDsl + fun box( + entity: Entity, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + ) { + filled(entity.boundingBox, color, sides) + outline(entity.boundingBox, color, sides, mode) + } + + @ShapeDsl + fun filled(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { + box.pair?.second?.let { + esp.getBuilder(it.minX, it.minY, it.minZ).filled(box, color, sides) + } + } + + @ShapeDsl + fun outline( + box: DynamicAABB, + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + box.pair?.second?.let { + esp.getBuilder(it.minX, it.minY, it.minZ).outline(box, color, sides, mode) + } + } + + @ShapeDsl + fun box( + box: DynamicAABB, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + box.pair?.second?.let { + esp.getBuilder(it.minX, it.minY, it.minZ) + .box(box, filledColor, outlineColor, sides, mode) + } + } } diff --git a/src/main/kotlin/com/lambda/graphics/renderer/esp/Treed.kt b/src/main/kotlin/com/lambda/graphics/renderer/esp/Treed.kt deleted file mode 100644 index 42394ed93..000000000 --- a/src/main/kotlin/com/lambda/graphics/renderer/esp/Treed.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.renderer.esp - -import com.lambda.Lambda.mc -import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.gl.GlStateUtils -import com.lambda.graphics.pipeline.VertexBuilder -import com.lambda.graphics.pipeline.VertexPipeline -import com.lambda.graphics.shader.Shader -import com.lambda.module.modules.client.StyleEditor -import com.lambda.util.extension.partialTicks - -/** - * Open class for 3d rendering. It contains two pipelines, one for edges and the other for faces. - */ -open class Treed(private val static: Boolean) { - val shader = if (static) staticMode.first else dynamicMode.first - - val faces = VertexPipeline(VertexMode.Triangles, if (static) staticMode.second else dynamicMode.second) - val edges = VertexPipeline(VertexMode.Lines, if (static) staticMode.second else dynamicMode.second) - - var faceBuilder = VertexBuilder(); private set - var edgeBuilder = VertexBuilder(); private set - - fun upload() { - faces.upload(faceBuilder) - edges.upload(edgeBuilder) - } - - fun render() { - shader.use() - - if (!static) - shader["u_TickDelta"] = mc.partialTicks - - GlStateUtils.withFaceCulling(faces::render) - GlStateUtils.withLineWidth(StyleEditor.outlineWidth, edges::render) - } - - fun clear() { - faces.clear() - edges.clear() - - faceBuilder = VertexBuilder() - edgeBuilder = VertexBuilder() - } - - /** - * Public object for static rendering. Shapes rendering by this are not interpolated. - * That means that if the shape is frequently moving, its movement will saccade. - */ - object Static : Treed(true) - - /** - * Public object for dynamic rendering. Its position will be interpolated between ticks, allowing - * for smooth movement at the cost of duplicate position and slightly higher memory consumption. - */ - object Dynamic : Treed(false) - - companion object { - private val staticMode = Shader("shaders/vertex/box_static.glsl", "shaders/fragment/pos_color.glsl") to VertexAttrib.Group.STATIC_RENDERER - private val dynamicMode = Shader("shaders/vertex/box_dynamic.glsl", "shaders/fragment/pos_color.glsl") to VertexAttrib.Group.DYNAMIC_RENDERER - } -} diff --git a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 282c6fcd1..3c6e1ad55 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -21,10 +21,10 @@ import com.lambda.Lambda.mc import com.lambda.config.settings.collections.CollectionSetting.Companion.onDeselect import com.lambda.config.settings.collections.CollectionSetting.Companion.onSelect import com.lambda.context.SafeContext -import com.lambda.graphics.renderer.esp.ChunkedESP.Companion.newChunkedESP +import com.lambda.graphics.mc.ChunkedRegionESP.Companion.newChunkedRegionESP +import com.lambda.graphics.mc.RegionShapeBuilder import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh -import com.lambda.graphics.renderer.esp.ShapeBuilder import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe @@ -67,24 +67,29 @@ object BlockESP : Module( @JvmStatic val model: BlockStateModel get() = mc.bakedModelManager.missingModel - private val esp = newChunkedESP { world, position -> + private val esp = newChunkedRegionESP({ true }) { world, position -> val state = world.getBlockState(position) - if (state.block !in blocks) return@newChunkedESP + if (state.block !in blocks) return@newChunkedRegionESP - val sides = if (mesh) { - buildSideMesh(position) { - world.getBlockState(it).block in blocks - } - } else DirectionMask.ALL + val sides = + if (mesh) { + buildSideMesh(position) { world.getBlockState(it).block in blocks } + } else DirectionMask.ALL - build(state, position.toBlockPos(), sides) + runSafe { build(this@newChunkedRegionESP, state, position.toBlockPos(), sides) } } - private fun ShapeBuilder.build( + init { + onEnable { esp.rebuildAll() } + onDisable { esp.clear() } + } + + private fun SafeContext.build( + builder: RegionShapeBuilder, state: BlockState, pos: BlockPos, sides: Int, - ) = runSafe { + ) = with(builder) { val blockColor = blockColor(state, pos) if (drawFaces) filled(pos, state, if (useBlockColor) blockColor else faceColor, sides) From 3503e6473aab20aec39bb7f4e4fcde0084ef65a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emy=20=F0=9F=92=9C?= Date: Fri, 19 Dec 2025 17:28:09 -0500 Subject: [PATCH 13/20] fixed imgui flickering --- src/main/kotlin/com/lambda/gui/DearImGui.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/kotlin/com/lambda/gui/DearImGui.kt b/src/main/kotlin/com/lambda/gui/DearImGui.kt index 66dfda249..770468d52 100644 --- a/src/main/kotlin/com/lambda/gui/DearImGui.kt +++ b/src/main/kotlin/com/lambda/gui/DearImGui.kt @@ -36,6 +36,7 @@ import imgui.glfw.ImGuiImplGlfw import net.minecraft.client.gl.GlBackend import net.minecraft.client.texture.GlTexture import org.lwjgl.opengl.GL30.GL_FRAMEBUFFER +import org.lwjgl.opengl.GL32C import kotlin.math.abs object DearImGui : Loadable { @@ -120,6 +121,8 @@ object DearImGui : Loadable { GuiEvent.EndFrame.post() implGl3.renderDrawData(ImGui.getDrawData()) + + GlStateManager._glBindFramebuffer(GL_FRAMEBUFFER, 0) } fun destroy() { From ebbd322721aca0b86012874d0c5c8a0e02df6253 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Dec 2025 00:22:29 +0100 Subject: [PATCH 14/20] Dynamic interpolated ESP, splines, EntityESP --- .../kotlin/com/lambda/graphics/RenderMain.kt | 58 ++- .../kotlin/com/lambda/graphics/esp/EspDsl.kt | 32 ++ .../com/lambda/graphics/esp/RegionESP.kt | 126 +++++ .../com/lambda/graphics/esp/ShapeScope.kt | 403 +++++++++++++++ .../lambda/graphics/mc/ChunkedRegionESP.kt | 205 ++------ .../com/lambda/graphics/mc/CurveUtils.kt | 247 ++++++++++ .../com/lambda/graphics/mc/ImGuiWorldText.kt | 172 +++++++ .../graphics/mc/InterpolatedRegionESP.kt | 139 ++++++ .../graphics/mc/LambdaRenderPipelines.kt | 4 +- .../com/lambda/graphics/mc/RegionRenderer.kt | 209 ++++---- .../lambda/graphics/mc/RegionShapeBuilder.kt | 458 +++++++++++++++++- .../graphics/mc/RegionVertexCollector.kt | 70 +-- .../com/lambda/graphics/mc/RenderRegion.kt | 52 +- .../lambda/graphics/mc/TransientRegionESP.kt | 101 +--- .../lambda/graphics/mc/WorldTextRenderer.kt | 307 ++++++++++++ .../lambda/graphics/renderer/esp/ShapeDsl.kt | 61 +-- .../lambda/module/modules/render/BlockESP.kt | 46 +- .../lambda/module/modules/render/EntityESP.kt | 345 +++++++++++++ .../module/modules/render/StorageESP.kt | 283 ++++++----- 19 files changed, 2651 insertions(+), 667 deletions(-) create mode 100644 src/main/kotlin/com/lambda/graphics/esp/EspDsl.kt create mode 100644 src/main/kotlin/com/lambda/graphics/esp/RegionESP.kt create mode 100644 src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/CurveUtils.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/ImGuiWorldText.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/InterpolatedRegionESP.kt create mode 100644 src/main/kotlin/com/lambda/graphics/mc/WorldTextRenderer.kt create mode 100644 src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt diff --git a/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/src/main/kotlin/com/lambda/graphics/RenderMain.kt index bbd9b7c14..2a1ac695f 100644 --- a/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -17,19 +17,25 @@ package com.lambda.graphics +import com.lambda.Lambda.mc import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrices -import com.lambda.graphics.mc.LambdaRenderPipelines import com.lambda.graphics.mc.TransientRegionESP +import net.minecraft.util.math.Vec3d import org.joml.Matrix4f +import org.joml.Vector2f +import org.joml.Vector4f object RenderMain { - val StaticESP = TransientRegionESP("Static") - val DynamicESP = TransientRegionESP("Dynamic") + @JvmStatic + val StaticESP = TransientRegionESP("Static", true) + + @JvmStatic + val DynamicESP = TransientRegionESP("Dynamic", false) val projectionMatrix = Matrix4f() val modelViewMatrix @@ -37,14 +43,56 @@ object RenderMain { val projModel: Matrix4f get() = Matrix4f(projectionMatrix).mul(modelViewMatrix) + /** + * Project a world position to screen coordinates. Returns null if the position is behind the + * camera or off-screen. + * + * @param worldPos The world position to project + * @return Screen coordinates (x, y) in pixels, or null if not visible + */ + fun worldToScreen(worldPos: Vec3d): Vector2f? { + val camera = mc.gameRenderer?.camera ?: return null + val cameraPos = camera.pos + + // Camera-relative position + val relX = (worldPos.x - cameraPos.x).toFloat() + val relY = (worldPos.y - cameraPos.y).toFloat() + val relZ = (worldPos.z - cameraPos.z).toFloat() + + // Apply projection * modelview matrix + val vec = Vector4f(relX, relY, relZ, 1f) + projModel.transform(vec) + + // Behind camera check + if (vec.w <= 0) return null + + // Perspective divide to get NDC + val ndcX = vec.x / vec.w + val ndcY = vec.y / vec.w + val ndcZ = vec.z / vec.w + + // Off-screen check (NDC is -1 to 1) + if (ndcZ < -1 || ndcZ > 1) return null + + // NDC to screen coordinates (Y is flipped in screen space) + val window = mc.window + val screenX = (ndcX + 1f) * 0.5f * window.framebufferWidth + val screenY = (1f - ndcY) * 0.5f * window.framebufferHeight + + return Vector2f(screenX, screenY) + } + + /** Check if a world position is visible on screen. */ + fun isOnScreen(worldPos: Vec3d): Boolean = worldToScreen(worldPos) != null + @JvmStatic fun render3D(positionMatrix: Matrix4f, projMatrix: Matrix4f) { resetMatrices(positionMatrix) projectionMatrix.set(projMatrix) // Render transient ESPs using the new pipeline - StaticESP.render(false) // Depth tested - DynamicESP.render(true) // Through walls + StaticESP.render() // Uses internal depthTest flag (true) + DynamicESP.render() // Uses internal depthTest flag (false) RenderEvent.Render.post() } diff --git a/src/main/kotlin/com/lambda/graphics/esp/EspDsl.kt b/src/main/kotlin/com/lambda/graphics/esp/EspDsl.kt new file mode 100644 index 000000000..0c0a19a3f --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/esp/EspDsl.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.esp + +import com.lambda.graphics.mc.ChunkedRegionESP +import com.lambda.module.Module + +@DslMarker +annotation class EspDsl + +fun Module.chunkedEsp( + name: String, + depthTest: Boolean = false, + update: ShapeScope.(net.minecraft.world.World, com.lambda.util.world.FastVector) -> Unit +): ChunkedRegionESP { + return ChunkedRegionESP(this, name, depthTest, update) +} diff --git a/src/main/kotlin/com/lambda/graphics/esp/RegionESP.kt b/src/main/kotlin/com/lambda/graphics/esp/RegionESP.kt new file mode 100644 index 000000000..b32e03755 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/esp/RegionESP.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.esp + +import com.lambda.Lambda.mc +import com.lambda.graphics.mc.LambdaRenderPipelines +import com.lambda.graphics.mc.RegionRenderer +import com.lambda.graphics.mc.RenderRegion +import com.lambda.util.extension.tickDelta +import com.mojang.blaze3d.systems.RenderSystem +import java.util.concurrent.ConcurrentHashMap +import kotlin.math.floor +import org.joml.Matrix4f +import org.joml.Vector3f +import org.joml.Vector4f + +/** + * Base class for region-based ESP systems. Provides unified rendering logic and region management. + */ +abstract class RegionESP(val name: String, val depthTest: Boolean) { + protected val renderers = ConcurrentHashMap() + + /** Get or create a ShapeScope for a specific world position. */ + open fun shapes(x: Double, y: Double, z: Double, block: ShapeScope.() -> Unit) {} + + /** Upload collected geometry to GPU. Must be called on main thread. */ + open fun upload() {} + + /** Clear all geometry data. */ + abstract fun clear() + + /** Close and release all GPU resources. */ + open fun close() { + renderers.values.forEach { it.close() } + renderers.clear() + clear() + } + + /** + * Render all active regions. + * @param tickDelta Progress within current tick (used for interpolation) + */ + open fun render(tickDelta: Float = mc.tickDelta) { + val camera = mc.gameRenderer?.camera ?: return + val cameraPos = camera.pos + + val activeRenderers = renderers.values.filter { it.hasData() } + if (activeRenderers.isEmpty()) return + + val modelViewMatrix = com.lambda.graphics.RenderMain.modelViewMatrix + val transforms = activeRenderers.map { renderer -> + val offset = renderer.region.computeCameraRelativeOffset(cameraPos) + val modelView = Matrix4f(modelViewMatrix).translate(offset) + + val dynamicTransform = RenderSystem.getDynamicUniforms() + .write( + modelView, + Vector4f(1f, 1f, 1f, 1f), + Vector3f(0f, 0f, 0f), + Matrix4f() + ) + renderer to dynamicTransform + } + + // Render Faces + RegionRenderer.createRenderPass("$name Faces")?.use { pass -> + val pipeline = + if (depthTest) LambdaRenderPipelines.ESP_QUADS + else LambdaRenderPipelines.ESP_QUADS_THROUGH + pass.setPipeline(pipeline) + RenderSystem.bindDefaultUniforms(pass) + transforms.forEach { (renderer, transform) -> + pass.setUniform("DynamicTransforms", transform) + renderer.renderFaces(pass) + } + } + + // Render Edges + RegionRenderer.createRenderPass("$name Edges")?.use { pass -> + val pipeline = + if (depthTest) LambdaRenderPipelines.ESP_LINES + else LambdaRenderPipelines.ESP_LINES_THROUGH + pass.setPipeline(pipeline) + RenderSystem.bindDefaultUniforms(pass) + transforms.forEach { (renderer, transform) -> + pass.setUniform("DynamicTransforms", transform) + renderer.renderEdges(pass) + } + } + } + + /** + * Compute a unique key for a region based on its coordinates. Prevents collisions between + * regions at different Y levels. + */ + protected fun getRegionKey(x: Double, y: Double, z: Double): Long { + val rx = (RenderRegion.REGION_SIZE * floor(x / RenderRegion.REGION_SIZE)).toInt() + val ry = (RenderRegion.REGION_SIZE * floor(y / RenderRegion.REGION_SIZE)).toInt() + val rz = (RenderRegion.REGION_SIZE * floor(z / RenderRegion.REGION_SIZE)).toInt() + + return getRegionKey(rx, ry, rz) + } + + protected fun getRegionKey(rx: Int, ry: Int, rz: Int): Long { + // 20 bits for X, 20 bits for Z, 24 bits for Y (total 64) + // This supports +- 500k blocks in X/Z and full Y range + return (rx.toLong() and 0xFFFFF) or + ((rz.toLong() and 0xFFFFF) shl 20) or + ((ry.toLong() and 0xFFFFFF) shl 40) + } +} diff --git a/src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt b/src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt new file mode 100644 index 000000000..93705b4ec --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt @@ -0,0 +1,403 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.esp + +import com.lambda.graphics.mc.RegionShapeBuilder +import com.lambda.graphics.mc.RegionVertexCollector +import com.lambda.graphics.mc.RenderRegion +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.DynamicAABB +import net.minecraft.block.BlockState +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.Vec3d +import net.minecraft.util.shape.VoxelShape +import java.awt.Color + +@EspDsl +class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { + internal val builder = RegionShapeBuilder(region) + internal val shapes = if (collectShapes) mutableListOf() else null + + /** Start building a box. */ + fun box(box: Box, id: Any? = null, block: BoxScope.() -> Unit) { + val scope = BoxScope(box, this) + scope.apply(block) + if (collectShapes) { + shapes?.add( + EspShape.BoxShape( + id?.hashCode() ?: box.hashCode(), + box, + scope.filledColor, + scope.outlineColor, + scope.sides, + scope.outlineMode + ) + ) + } + } + + /** Draw a line between two points. */ + fun line(start: Vec3d, end: Vec3d, color: Color, width: Float = 1.0f, id: Any? = null) { + builder.line(start, end, color, width) + if (collectShapes) { + shapes?.add( + EspShape.LineShape( + id?.hashCode() ?: (start.hashCode() xor end.hashCode()), + start, + end, + color, + width + ) + ) + } + } + + /** Draw a tracer. */ + fun tracer(from: Vec3d, to: Vec3d, id: Any? = null, block: LineScope.() -> Unit = {}) { + val scope = LineScope(from, to, this) + scope.apply(block) + scope.draw() + if (collectShapes) { + shapes?.add( + EspShape.LineShape( + id?.hashCode() ?: (from.hashCode() xor to.hashCode()), + from, + to, + scope.lineColor, + scope.lineWidth, + scope.lineDashLength, + scope.lineGapLength + ) + ) + } + } + + /** Draw a simple filled box. */ + fun filled(box: Box, color: Color, sides: Int = DirectionMask.ALL) { + builder.filled(box, color, sides) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(box.hashCode(), box, color, null, sides)) + } + } + + /** Draw a simple outlined box. */ + fun outline(box: Box, color: Color, sides: Int = DirectionMask.ALL) { + builder.outline(box, color, sides) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(box.hashCode(), box, null, color, sides)) + } + } + + fun filled(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { + builder.filled(box, color, sides) + if (collectShapes) { + box.pair?.second?.let { + shapes?.add(EspShape.BoxShape(it.hashCode(), it, color, null, sides)) + } + } + } + + fun outline(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { + builder.outline(box, color, sides) + if (collectShapes) { + box.pair?.second?.let { + shapes?.add(EspShape.BoxShape(it.hashCode(), it, null, color, sides)) + } + } + } + + fun filled(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) { + builder.filled(pos, color, sides) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), color, null, sides)) + } + } + + fun outline(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) { + builder.outline(pos, color, sides) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), null, color, sides)) + } + } + + fun filled(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) { + builder.filled(pos, state, color, sides) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), color, null, sides)) + } + } + + fun outline(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) { + builder.outline(pos, state, color, sides) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), null, color, sides)) + } + } + + fun filled(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) { + builder.filled(shape, color, sides) + if (collectShapes) { + shape.boundingBoxes.forEach { + shapes?.add(EspShape.BoxShape(it.hashCode(), it, color, null, sides)) + } + } + } + + fun outline(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) { + builder.outline(shape, color, sides) + if (collectShapes) { + shape.boundingBoxes.forEach { + shapes?.add(EspShape.BoxShape(it.hashCode(), it, null, color, sides)) + } + } + } + + fun box( + pos: BlockPos, + state: BlockState, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + builder.box(pos, state, filled, outline, sides, mode) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), filled, outline, sides, mode)) + } + } + + fun box( + pos: BlockPos, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + builder.box(pos, filled, outline, sides, mode) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), filled, outline, sides, mode)) + } + } + + fun box( + box: Box, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + builder.box(box, filledColor, outlineColor, sides, mode) + if (collectShapes) { + shapes?.add(EspShape.BoxShape(box.hashCode(), box, filledColor, outlineColor, sides, mode)) + } + } + + fun box( + box: DynamicAABB, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + builder.box(box, filledColor, outlineColor, sides, mode) + if (collectShapes) { + box.pair?.second?.let { + shapes?.add( + EspShape.BoxShape(it.hashCode(), it, filledColor, outlineColor, sides, mode) + ) + } + } + } + + fun box( + entity: net.minecraft.block.entity.BlockEntity, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + builder.box(entity, filled, outline, sides, mode) + if (collectShapes) { + shapes?.add( + EspShape.BoxShape( + entity.pos.hashCode(), + Box(entity.pos), + filled, + outline, + sides, + mode + ) + ) + } + } + + fun box( + entity: net.minecraft.entity.Entity, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + builder.box(entity, filled, outline, sides, mode) + if (collectShapes) { + shapes?.add( + EspShape.BoxShape( + entity.hashCode(), + entity.boundingBox, + filled, + outline, + sides, + mode + ) + ) + } + } +} + +@EspDsl +class BoxScope(val box: Box, val parent: ShapeScope) { + internal var filledColor: Color? = null + internal var outlineColor: Color? = null + internal var sides: Int = DirectionMask.ALL + internal var outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + + fun filled(color: Color, sides: Int = DirectionMask.ALL) { + this.filledColor = color + this.sides = sides + parent.builder.filled(box, color, sides) + } + + fun outline( + color: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + this.outlineColor = color + this.sides = sides + this.outlineMode = mode + parent.builder.outline(box, color, sides, mode) + } +} + +@EspDsl +class LineScope(val from: Vec3d, val to: Vec3d, val parent: ShapeScope) { + internal var lineColor: Color = Color.WHITE + internal var lineWidth: Float = 1.0f + internal var lineDashLength: Double? = null + internal var lineGapLength: Double? = null + + fun color(color: Color) { + this.lineColor = color + } + + fun width(width: Float) { + this.lineWidth = width + } + + fun dashed(dashLength: Double = 0.5, gapLength: Double = 0.25) { + this.lineDashLength = dashLength + this.lineGapLength = gapLength + } + + internal fun draw() { + val dLen = lineDashLength + val gLen = lineGapLength + + if (dLen != null && gLen != null) { + parent.builder.dashedLine(from, to, lineColor, dLen, gLen, lineWidth) + } else { + parent.builder.line(from, to, lineColor, lineWidth) + } + } +} + +sealed class EspShape(val id: Int) { + abstract fun renderInterpolated( + prev: EspShape, + tickDelta: Float, + collector: RegionVertexCollector, + region: RenderRegion + ) + + class BoxShape( + id: Int, + val box: Box, + val filledColor: Color?, + val outlineColor: Color?, + val sides: Int = DirectionMask.ALL, + val outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) : EspShape(id) { + override fun renderInterpolated( + prev: EspShape, + tickDelta: Float, + collector: RegionVertexCollector, + region: RenderRegion + ) { + val interpBox = + if (prev is BoxShape) { + Box( + MathHelper.lerp(tickDelta.toDouble(), prev.box.minX, box.minX), + MathHelper.lerp(tickDelta.toDouble(), prev.box.minY, box.minY), + MathHelper.lerp(tickDelta.toDouble(), prev.box.minZ, box.minZ), + MathHelper.lerp(tickDelta.toDouble(), prev.box.maxX, box.maxX), + MathHelper.lerp(tickDelta.toDouble(), prev.box.maxY, box.maxY), + MathHelper.lerp(tickDelta.toDouble(), prev.box.maxZ, box.maxZ) + ) + } else box + + val shapeBuilder = RegionShapeBuilder(region) + filledColor?.let { shapeBuilder.filled(interpBox, it, sides) } + outlineColor?.let { shapeBuilder.outline(interpBox, it, sides, outlineMode) } + + collector.faceVertices.addAll(shapeBuilder.collector.faceVertices) + collector.edgeVertices.addAll(shapeBuilder.collector.edgeVertices) + } + } + + class LineShape( + id: Int, + val from: Vec3d, + val to: Vec3d, + val color: Color, + val width: Float, + val dashLength: Double? = null, + val gapLength: Double? = null + ) : EspShape(id) { + override fun renderInterpolated( + prev: EspShape, + tickDelta: Float, + collector: RegionVertexCollector, + region: RenderRegion + ) { + val iFrom = if (prev is LineShape) prev.from.lerp(from, tickDelta.toDouble()) else from + val iTo = if (prev is LineShape) prev.to.lerp(to, tickDelta.toDouble()) else to + + val shapeBuilder = RegionShapeBuilder(region) + if (dashLength != null && gapLength != null) { + shapeBuilder.dashedLine(iFrom, iTo, color, dashLength, gapLength, width) + } else { + shapeBuilder.line(iFrom, iTo, color, width) + } + + collector.faceVertices.addAll(shapeBuilder.collector.faceVertices) + collector.edgeVertices.addAll(shapeBuilder.collector.edgeVertices) + } + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt b/src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt index 479314688..d367694a5 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/ChunkedRegionESP.kt @@ -17,28 +17,20 @@ package com.lambda.graphics.mc -import com.lambda.Lambda.mc import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.SafeListener.Companion.listenConcurrently +import com.lambda.graphics.esp.RegionESP +import com.lambda.graphics.esp.ShapeScope import com.lambda.module.Module import com.lambda.module.modules.client.StyleEditor import com.lambda.threading.runSafe import com.lambda.util.world.FastVector import com.lambda.util.world.fastVectorOf -import com.mojang.blaze3d.buffers.GpuBuffer -import com.mojang.blaze3d.systems.RenderPass -import com.mojang.blaze3d.systems.RenderSystem -import com.mojang.blaze3d.vertex.VertexFormat import net.minecraft.world.World import net.minecraft.world.chunk.WorldChunk -import org.joml.Matrix4f -import org.joml.Quaternionf -import org.joml.Vector3f -import org.joml.Vector4f -import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedDeque @@ -47,23 +39,25 @@ import java.util.concurrent.ConcurrentLinkedDeque * * This system: * - Uses region-relative coordinates for precision-safe rendering - * - Uses MC's RenderPass and BufferBuilder system * - Maintains per-chunk geometry for efficient updates - * - Supports both depth-tested and through-wall rendering * * @param owner The module that owns this ESP system - * @param throughWalls Whether to render through walls + * @param name The name of the ESP system + * @param depthTest Whether to use depth testing * @param update The update function called for each block position */ -class ChunkedRegionESP private constructor( +class ChunkedRegionESP( owner: Module, - private val throughWalls: () -> Boolean, - private val update: RegionShapeBuilder.(World, FastVector) -> Unit -) { + name: String, + depthTest: Boolean = false, + private val update: ShapeScope.(World, FastVector) -> Unit +) : RegionESP(name, depthTest) { private val chunkMap = ConcurrentHashMap() private val WorldChunk.regionChunk - get() = chunkMap.getOrPut(pos.toLong()) { RegionChunk(this, this@ChunkedRegionESP) } + get() = chunkMap.getOrPut(getRegionKey(pos.x shl 4, bottomY, pos.z shl 4)) { + RegionChunk(this) + } private val uploadQueue = ConcurrentLinkedDeque<() -> Unit>() private val rebuildQueue = ConcurrentLinkedDeque() @@ -87,8 +81,7 @@ class ChunkedRegionESP private constructor( } } - /** Clear all chunk data. */ - fun clear() { + override fun clear() { chunkMap.values.forEach { it.close() } chunkMap.clear() rebuildQueue.clear() @@ -112,201 +105,59 @@ class ChunkedRegionESP private constructor( owner.listen { event -> event.chunk.regionChunk.markDirty() } owner.listen { - val pos = it.chunk.pos.toLong() + val pos = getRegionKey(it.chunk.pos.x shl 4, it.chunk.bottomY, it.chunk.pos.z shl 4) chunkMap.remove(pos)?.close() } owner.listenConcurrently { val queueSize = rebuildQueue.size val polls = minOf(StyleEditor.rebuildsPerTick, queueSize) - repeat(polls) { - rebuildQueue.poll()?.rebuild() - } + repeat(polls) { rebuildQueue.poll()?.rebuild() } } owner.listen { val polls = minOf(StyleEditor.uploadsPerTick, uploadQueue.size) - repeat(polls) { - uploadQueue.poll()?.invoke() - } + repeat(polls) { uploadQueue.poll()?.invoke() } } owner.listen { render() } } - /** Render all chunks with geometry. */ - private fun render() { - val camera = mc.gameRenderer?.camera ?: return - val cameraPos = camera.pos - val framebuffer = mc.framebuffer ?: return - - val chunksWithData = chunkMap.values.filter { it.hasData } - if (chunksWithData.isEmpty()) return - - val chunkTransforms = - chunksWithData.map { chunk -> - val offset = chunk.region.computeCameraRelativeOffset(cameraPos) - val rotation = camera.rotation.conjugate(Quaternionf()) - val modelView = Matrix4f().rotation(rotation).translate(offset) - val transforms = RenderSystem.getDynamicUniforms().write( - modelView, - Vector4f(1.0f, 1.0f, 1.0f, 1.0f), // color modulator - Vector3f( - 0f, - 0f, - 0f - ), // model offset - ignored by vanilla shaders! - Matrix4f() // texture matrix - ) - chunk to transforms - } - - RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass( - { "Lambda ESP Faces" }, - framebuffer.colorAttachmentView, - OptionalInt.empty(), - framebuffer.depthAttachmentView, - OptionalDouble.empty() - ) - .use { renderPass -> - val pipeline = - if (throughWalls()) LambdaRenderPipelines.ESP_QUADS_THROUGH - else LambdaRenderPipelines.ESP_QUADS - renderPass.setPipeline(pipeline) - RenderSystem.bindDefaultUniforms(renderPass) - - chunkTransforms.forEach { (chunk, transforms) -> - renderPass.setUniform("DynamicTransforms", transforms) - chunk.renderFaces(renderPass) - } - } - - RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass( - { "Lambda ESP Edges" }, - framebuffer.colorAttachmentView, - OptionalInt.empty(), - framebuffer.depthAttachmentView, - OptionalDouble.empty() - ) - .use { renderPass -> - val pipeline = - if (throughWalls()) LambdaRenderPipelines.ESP_LINES_THROUGH - else LambdaRenderPipelines.ESP_LINES - renderPass.setPipeline(pipeline) - RenderSystem.bindDefaultUniforms(renderPass) - - chunkTransforms.forEach { (chunk, transforms) -> - renderPass.setUniform("DynamicTransforms", transforms) - chunk.renderEdges(renderPass) - } - } - } - - companion object { - /** - * Create a new chunked region ESP for a module. - * - * @param throughWalls Whether to render through walls - * @param update The update function called for each block position - */ - fun Module.newChunkedRegionESP( - throughWalls: () -> Boolean = { false }, - update: RegionShapeBuilder.(World, FastVector) -> Unit - ) = ChunkedRegionESP(this@newChunkedRegionESP, throughWalls, update) - } - /** Per-chunk rendering data. */ - private inner class RegionChunk(val chunk: WorldChunk, val owner: ChunkedRegionESP) { + private inner class RegionChunk(val chunk: WorldChunk) { val region = RenderRegion.forChunk(chunk.pos.x, chunk.pos.z, chunk.bottomY) - - private var faceBuffer: GpuBuffer? = null - private var edgeBuffer: GpuBuffer? = null - private var faceIndexCount = 0 - private var edgeIndexCount = 0 - - var hasData = false - private set + private val key = getRegionKey(chunk.pos.x shl 4, chunk.bottomY, chunk.pos.z shl 4) private var isDirty = false fun markDirty() { isDirty = true - if (!owner.rebuildQueue.contains(this)) { - owner.rebuildQueue.add(this) + if (!rebuildQueue.contains(this)) { + rebuildQueue.add(this) } } - /** - * Rebuild this chunk's geometry. Runs on a background thread - collects vertices into - * thread-safe collections. - */ fun rebuild() { if (!isDirty) return - val builder = RegionShapeBuilder(region) + val scope = ShapeScope(region) - var blockCount = 0 for (x in chunk.pos.startX..chunk.pos.endX) { for (z in chunk.pos.startZ..chunk.pos.endZ) { for (y in chunk.bottomY..chunk.height) { - owner.update(builder, chunk.world, fastVectorOf(x, y, z)) - blockCount++ + update(scope, chunk.world, fastVectorOf(x, y, z)) } } } - owner.uploadQueue.add { upload(builder.collector) } - } - - /** Upload collected vertices to GPU. Must run on the main/render thread. */ - private fun upload(collector: RegionVertexCollector) { - faceBuffer?.close() - edgeBuffer?.close() - val result = collector.upload() - - faceBuffer = result.faces?.buffer - faceIndexCount = result.faces?.indexCount ?: 0 - - edgeBuffer = result.edges?.buffer - edgeIndexCount = result.edges?.indexCount ?: 0 - - hasData = faceBuffer != null || edgeBuffer != null - isDirty = false - } - - fun renderFaces(renderPass: RenderPass) { - val buffer = faceBuffer ?: return - if (faceIndexCount == 0) return - - renderPass.setVertexBuffer(0, buffer) - val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS) - val indexBuffer = shapeIndexBuffer.getIndexBuffer(faceIndexCount) - - renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) - renderPass.drawIndexed(0, 0, faceIndexCount, 1) - } - - fun renderEdges(renderPass: RenderPass) { - val buffer = edgeBuffer ?: return - if (edgeIndexCount == 0) return - - renderPass.setVertexBuffer(0, buffer) - val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.LINES) - val indexBuffer = shapeIndexBuffer.getIndexBuffer(edgeIndexCount) - - renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) - renderPass.drawIndexed(0, 0, edgeIndexCount, 1) + uploadQueue.add { + val renderer = renderers.getOrPut(key) { RegionRenderer(region) } + renderer.upload(scope.builder.collector) + isDirty = false + } } fun close() { - faceBuffer?.close() - edgeBuffer?.close() - faceBuffer = null - edgeBuffer = null - hasData = false + renderers.remove(key)?.close() } } } diff --git a/src/main/kotlin/com/lambda/graphics/mc/CurveUtils.kt b/src/main/kotlin/com/lambda/graphics/mc/CurveUtils.kt new file mode 100644 index 000000000..2cf2b1ddc --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/CurveUtils.kt @@ -0,0 +1,247 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.Vec3d + +/** + * Utility functions for curve and spline calculations. + * + * Provides Bezier curves and Catmull-Rom splines for smooth + * trajectory rendering and path visualization. + */ +object CurveUtils { + + /** + * Linear interpolation between two points. + */ + fun lerp(t: Double, p0: Vec3d, p1: Vec3d): Vec3d { + return Vec3d( + MathHelper.lerp(t, p0.x, p1.x), + MathHelper.lerp(t, p0.y, p1.y), + MathHelper.lerp(t, p0.z, p1.z) + ) + } + + /** + * Quadratic Bezier curve. + * + * B(t) = (1-t)²P0 + 2(1-t)tP1 + t²P2 + * + * @param t Parameter from 0.0 to 1.0 + * @param p0 Start point + * @param p1 Control point + * @param p2 End point + */ + fun quadraticBezier(t: Double, p0: Vec3d, p1: Vec3d, p2: Vec3d): Vec3d { + val mt = 1.0 - t + val mt2 = mt * mt + val t2 = t * t + + return Vec3d( + mt2 * p0.x + 2 * mt * t * p1.x + t2 * p2.x, + mt2 * p0.y + 2 * mt * t * p1.y + t2 * p2.y, + mt2 * p0.z + 2 * mt * t * p1.z + t2 * p2.z + ) + } + + /** + * Cubic Bezier curve. + * + * B(t) = (1-t)³P0 + 3(1-t)²tP1 + 3(1-t)t²P2 + t³P3 + * + * @param t Parameter from 0.0 to 1.0 + * @param p0 Start point + * @param p1 First control point + * @param p2 Second control point + * @param p3 End point + */ + fun cubicBezier(t: Double, p0: Vec3d, p1: Vec3d, p2: Vec3d, p3: Vec3d): Vec3d { + val mt = 1.0 - t + val mt2 = mt * mt + val mt3 = mt2 * mt + val t2 = t * t + val t3 = t2 * t + + return Vec3d( + mt3 * p0.x + 3 * mt2 * t * p1.x + 3 * mt * t2 * p2.x + t3 * p3.x, + mt3 * p0.y + 3 * mt2 * t * p1.y + 3 * mt * t2 * p2.y + t3 * p3.y, + mt3 * p0.z + 3 * mt2 * t * p1.z + 3 * mt * t2 * p2.z + t3 * p3.z + ) + } + + /** + * Catmull-Rom spline interpolation. + * + * Unlike Bezier curves, Catmull-Rom splines pass through all control points. + * Uses MC's built-in catmullRom function. + * + * @param t Parameter from 0.0 to 1.0 (interpolates between p1 and p2) + * @param p0 Point before the segment + * @param p1 Start of segment + * @param p2 End of segment + * @param p3 Point after the segment + */ + fun catmullRom(t: Float, p0: Vec3d, p1: Vec3d, p2: Vec3d, p3: Vec3d): Vec3d { + return Vec3d( + MathHelper.catmullRom(t, p0.x.toFloat(), p1.x.toFloat(), p2.x.toFloat(), p3.x.toFloat()).toDouble(), + MathHelper.catmullRom(t, p0.y.toFloat(), p1.y.toFloat(), p2.y.toFloat(), p3.y.toFloat()).toDouble(), + MathHelper.catmullRom(t, p0.z.toFloat(), p1.z.toFloat(), p2.z.toFloat(), p3.z.toFloat()).toDouble() + ) + } + + /** + * Generate points along a quadratic Bezier curve. + * + * @param p0 Start point + * @param p1 Control point + * @param p2 End point + * @param segments Number of line segments + * @return List of points along the curve + */ + fun quadraticBezierPoints(p0: Vec3d, p1: Vec3d, p2: Vec3d, segments: Int): List { + return (0..segments).map { i -> + val t = i.toDouble() / segments + quadraticBezier(t, p0, p1, p2) + } + } + + /** + * Generate points along a cubic Bezier curve. + * + * @param p0 Start point + * @param p1 First control point + * @param p2 Second control point + * @param p3 End point + * @param segments Number of line segments + * @return List of points along the curve + */ + fun cubicBezierPoints(p0: Vec3d, p1: Vec3d, p2: Vec3d, p3: Vec3d, segments: Int): List { + return (0..segments).map { i -> + val t = i.toDouble() / segments + cubicBezier(t, p0, p1, p2, p3) + } + } + + /** + * Generate points along a Catmull-Rom spline that passes through all control points. + * + * @param controlPoints List of points the spline should pass through (minimum 4) + * @param segmentsPerSection Number of segments between each pair of control points + * @return List of points along the spline + */ + fun catmullRomSplinePoints(controlPoints: List, segmentsPerSection: Int): List { + if (controlPoints.size < 4) return controlPoints + + val result = mutableListOf() + + for (i in 1 until controlPoints.size - 2) { + val p0 = controlPoints[i - 1] + val p1 = controlPoints[i] + val p2 = controlPoints[i + 1] + val p3 = controlPoints[i + 2] + + for (j in 0 until segmentsPerSection) { + val t = j.toFloat() / segmentsPerSection + result.add(catmullRom(t, p0, p1, p2, p3)) + } + } + + // Add the last point + result.add(controlPoints[controlPoints.size - 2]) + + return result + } + + /** + * Estimate the arc length of a cubic Bezier curve using subdivision. + * + * @param p0 Start point + * @param p1 First control point + * @param p2 Second control point + * @param p3 End point + * @param subdivisions Number of subdivisions for estimation + */ + fun cubicBezierLength(p0: Vec3d, p1: Vec3d, p2: Vec3d, p3: Vec3d, subdivisions: Int = 32): Double { + var length = 0.0 + var prev = p0 + + for (i in 1..subdivisions) { + val t = i.toDouble() / subdivisions + val curr = cubicBezier(t, p0, p1, p2, p3) + length += prev.distanceTo(curr) + prev = curr + } + + return length + } + + /** + * Calculate the tangent (direction) at a point on a cubic Bezier curve. + * + * B'(t) = 3(1-t)²(P1-P0) + 6(1-t)t(P2-P1) + 3t²(P3-P2) + * + * @param t Parameter from 0.0 to 1.0 + * @param p0 Start point + * @param p1 First control point + * @param p2 Second control point + * @param p3 End point + * @return Normalized tangent vector + */ + fun cubicBezierTangent(t: Double, p0: Vec3d, p1: Vec3d, p2: Vec3d, p3: Vec3d): Vec3d { + val mt = 1.0 - t + val mt2 = mt * mt + val t2 = t * t + + val d1 = p1.subtract(p0) + val d2 = p2.subtract(p1) + val d3 = p3.subtract(p2) + + return Vec3d( + 3 * mt2 * d1.x + 6 * mt * t * d2.x + 3 * t2 * d3.x, + 3 * mt2 * d1.y + 6 * mt * t * d2.y + 3 * t2 * d3.y, + 3 * mt2 * d1.z + 6 * mt * t * d2.z + 3 * t2 * d3.z + ).normalize() + } + + /** + * Create a smooth path through waypoints using Catmull-Rom splines. + * Automatically handles the first and last points by duplicating them. + * + * @param waypoints List of points to pass through (minimum 2) + * @param segmentsPerSection Smoothness (higher = smoother) + */ + fun smoothPath(waypoints: List, segmentsPerSection: Int = 16): List { + if (waypoints.size < 2) return waypoints + if (waypoints.size == 2) return listOf(waypoints[0], waypoints[1]) + + // Extend with phantom points for natural curve at endpoints + val extended = buildList { + // Mirror first point + add(waypoints[0].add(waypoints[0].subtract(waypoints[1]))) + addAll(waypoints) + // Mirror last point + val last = waypoints.last() + val secondLast = waypoints[waypoints.size - 2] + add(last.add(last.subtract(secondLast))) + } + + return catmullRomSplinePoints(extended, segmentsPerSection) + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/ImGuiWorldText.kt b/src/main/kotlin/com/lambda/graphics/mc/ImGuiWorldText.kt new file mode 100644 index 000000000..735f17de1 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/ImGuiWorldText.kt @@ -0,0 +1,172 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.graphics.RenderMain +import imgui.ImGui +import imgui.ImVec2 +import net.minecraft.util.math.Vec3d +import java.awt.Color + +/** + * ImGUI-based world text renderer. + * Projects world coordinates to screen space and draws text using ImGUI. + * + * Usage: + * ```kotlin + * // In a GuiEvent.NewFrame listener + * ImGuiWorldText.drawText(entity.pos, "Label", Color.WHITE) + * ``` + */ +object ImGuiWorldText { + + /** + * Draw text at a world position using ImGUI. + * + * @param worldPos World position for the text + * @param text The text to render + * @param color Text color + * @param centered Whether to center the text horizontally + * @param offsetY Vertical offset in screen pixels (negative = up) + */ + fun drawText( + worldPos: Vec3d, + text: String, + color: Color = Color.WHITE, + centered: Boolean = true, + offsetY: Float = 0f + ) { + val screen = RenderMain.worldToScreen(worldPos) ?: return + + val drawList = ImGui.getBackgroundDrawList() + val colorInt = colorToImGui(color) + + val x = if (centered) { + val textSize = ImVec2() + ImGui.calcTextSize(textSize, text) + screen.x - textSize.x / 2f + } else { + screen.x + } + + drawList.addText(x, screen.y + offsetY, colorInt, text) + } + + /** + * Draw text with a shadow/outline effect. + */ + fun drawTextWithShadow( + worldPos: Vec3d, + text: String, + color: Color = Color.WHITE, + shadowColor: Color = Color.BLACK, + centered: Boolean = true, + offsetY: Float = 0f + ) { + val screen = RenderMain.worldToScreen(worldPos) ?: return + + val drawList = ImGui.getBackgroundDrawList() + val textSize = ImVec2() + ImGui.calcTextSize(textSize, text) + + val x = if (centered) screen.x - textSize.x / 2f else screen.x + val y = screen.y + offsetY + + // Draw shadow (offset by 1 pixel) + val shadowInt = colorToImGui(shadowColor) + drawList.addText(x + 1f, y + 1f, shadowInt, text) + + // Draw main text + val colorInt = colorToImGui(color) + drawList.addText(x, y, colorInt, text) + } + + /** + * Draw multiple lines of text stacked vertically. + */ + fun drawMultilineText( + worldPos: Vec3d, + lines: List, + color: Color = Color.WHITE, + centered: Boolean = true, + lineSpacing: Float = 12f, + offsetY: Float = 0f + ) { + val screen = RenderMain.worldToScreen(worldPos) ?: return + + val drawList = ImGui.getBackgroundDrawList() + val colorInt = colorToImGui(color) + + lines.forEachIndexed { index, line -> + val textSize = ImVec2() + ImGui.calcTextSize(textSize, line) + + val x = if (centered) screen.x - textSize.x / 2f else screen.x + val y = screen.y + offsetY + (index * lineSpacing) + + drawList.addText(x, y, colorInt, line) + } + } + + /** + * Draw text with a background box. + */ + fun drawTextWithBackground( + worldPos: Vec3d, + text: String, + textColor: Color = Color.WHITE, + backgroundColor: Color = Color(0, 0, 0, 128), + centered: Boolean = true, + padding: Float = 4f, + offsetY: Float = 0f + ) { + val screen = RenderMain.worldToScreen(worldPos) ?: return + + val drawList = ImGui.getBackgroundDrawList() + val textSize = ImVec2() + ImGui.calcTextSize(textSize, text) + + val x = if (centered) screen.x - textSize.x / 2f else screen.x + val y = screen.y + offsetY + + // Draw background + val bgInt = colorToImGui(backgroundColor) + drawList.addRectFilled( + x - padding, + y - padding, + x + textSize.x + padding, + y + textSize.y + padding, + bgInt, + 2f // corner rounding + ) + + // Draw text + val colorInt = colorToImGui(textColor) + drawList.addText(x, y, colorInt, text) + } + + /** + * Convert java.awt.Color to ImGui color format (ABGR) + */ + private fun colorToImGui(color: Color): Int { + return (color.alpha shl 24) or + (color.blue shl 16) or + (color.green shl 8) or + color.red + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/InterpolatedRegionESP.kt b/src/main/kotlin/com/lambda/graphics/mc/InterpolatedRegionESP.kt new file mode 100644 index 000000000..8b2b0b4b7 --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/InterpolatedRegionESP.kt @@ -0,0 +1,139 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.graphics.esp.RegionESP +import com.lambda.graphics.esp.ShapeScope +import java.util.concurrent.ConcurrentHashMap +import kotlin.math.floor + +/** + * Interpolated region-based ESP system for smooth entity rendering. + * + * Unlike TransientRegionESP which rebuilds every tick, this system stores both previous and current + * frame data and interpolates between them during rendering for smooth movement at any framerate. + */ +class InterpolatedRegionESP(name: String, depthTest: Boolean = false) : RegionESP(name, depthTest) { + // Current frame builders (being populated this tick) + private val currBuilders = ConcurrentHashMap() + + // Previous frame data (uploaded last tick) + private val prevBuilders = ConcurrentHashMap() + + // Interpolated collectors for rendering (computed each frame) + private val interpolatedCollectors = + ConcurrentHashMap() + + // Track if we need to re-interpolate + private var lastTickDelta = -1f + private var needsInterpolation = true + + override fun shapes(x: Double, y: Double, z: Double, block: ShapeScope.() -> Unit) { + val key = getRegionKey(x, y, z) + val scope = + currBuilders.getOrPut(key) { + val size = RenderRegion.REGION_SIZE + val rx = (size * floor(x / size)).toInt() + val ry = (size * floor(y / size)).toInt() + val rz = (size * floor(z / size)).toInt() + ShapeScope(RenderRegion(rx, ry, rz), collectShapes = true) + } + scope.apply(block) + } + + override fun clear() { + prevBuilders.clear() + currBuilders.clear() + interpolatedCollectors.clear() + } + + fun tick() { + prevBuilders.clear() + prevBuilders.putAll(currBuilders) + currBuilders.clear() + needsInterpolation = true + } + + override fun upload() { + needsInterpolation = true + } + + override fun render(tickDelta: Float) { + if (needsInterpolation || lastTickDelta != tickDelta) { + interpolate(tickDelta) + uploadInterpolated() + lastTickDelta = tickDelta + needsInterpolation = false + } + super.render(tickDelta) + } + + private fun interpolate(tickDelta: Float) { + interpolatedCollectors.clear() + (prevBuilders.keys + currBuilders.keys).toSet().forEach { key -> + val prevScope = prevBuilders[key] + val currScope = currBuilders[key] + val collector = RegionVertexCollector() + val region = currScope?.region ?: prevScope?.region ?: return@forEach + + val prevShapes = prevScope?.shapes?.associateBy { it.id } ?: emptyMap() + val currShapes = currScope?.shapes?.associateBy { it.id } ?: emptyMap() + + val allIds = (prevShapes.keys + currShapes.keys).toSet() + + for (id in allIds) { + val prev = prevShapes[id] + val curr = currShapes[id] + + when { + prev != null && curr != null -> { + curr.renderInterpolated(prev, tickDelta, collector, region) + } + curr != null -> { + // New shape - just render + curr.renderInterpolated(curr, 1.0f, collector, region) + } + prev != null -> { + // Disappeared - render at previous position + prev.renderInterpolated(prev, 1.0f, collector, region) + } + } + } + + if (collector.faceVertices.isNotEmpty() || collector.edgeVertices.isNotEmpty()) { + interpolatedCollectors[key] = collector + } + } + } + + private fun uploadInterpolated() { + val activeKeys = interpolatedCollectors.keys.toSet() + interpolatedCollectors.forEach { (key, collector) -> + val region = currBuilders[key]?.region ?: prevBuilders[key]?.region ?: return@forEach + + val renderer = renderers.getOrPut(key) { RegionRenderer(region) } + renderer.upload(collector) + } + + renderers.forEach { (key, renderer) -> + if (key !in activeKeys) { + renderer.clearData() + } + } + } +} diff --git a/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt index 3dcb4472c..eb070ef00 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt @@ -56,7 +56,7 @@ object LambdaRenderPipelines : Loadable { .withCull(false) .withVertexFormat( VertexFormats.POSITION_COLOR, - VertexFormat.DrawMode.TRIANGLES // ToDo: Should we use Triangles or Quads? + VertexFormat.DrawMode.QUADS ) .build() ) @@ -77,7 +77,7 @@ object LambdaRenderPipelines : Loadable { .withCull(false) .withVertexFormat( VertexFormats.POSITION_COLOR, - VertexFormat.DrawMode.TRIANGLES + VertexFormat.DrawMode.QUADS ) .build() ) diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt index fa8e96e87..1daba012d 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt @@ -34,109 +34,108 @@ import java.util.* */ class RegionRenderer(val region: RenderRegion) { - // Dedicated GPU buffers for faces and edges - private var faceVertexBuffer: GpuBuffer? = null - private var edgeVertexBuffer: GpuBuffer? = null - - // Index counts for draw calls - private var faceIndexCount = 0 - private var edgeIndexCount = 0 - - // State tracking - private var hasData = false - - /** - * Upload collected vertices from an external collector. This must be called on the main/render - * thread. - * - * @param collector The collector containing the geometry to upload - */ - fun upload(collector: RegionVertexCollector) { - val result = collector.upload() - - // Cleanup old buffers - faceVertexBuffer?.close() - edgeVertexBuffer?.close() - - // Assign new buffers and counts - faceVertexBuffer = result.faces?.buffer - faceIndexCount = result.faces?.indexCount ?: 0 - - edgeVertexBuffer = result.edges?.buffer - edgeIndexCount = result.edges?.indexCount ?: 0 - - hasData = faceVertexBuffer != null || edgeVertexBuffer != null - } - - /** - * Render faces using the given render pass. - * - * @param renderPass The active RenderPass to record commands into - */ - fun renderFaces(renderPass: RenderPass) { - val vb = faceVertexBuffer ?: return - if (faceIndexCount == 0) return - - renderPass.setVertexBuffer(0, vb) - // Use vanilla's sequential index buffer for quads - val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS) - val indexBuffer = shapeIndexBuffer.getIndexBuffer(faceIndexCount) - - renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) - renderPass.drawIndexed(0, 0, faceIndexCount, 1) - } - - /** - * Render edges using the given render pass. - * - * @param renderPass The active RenderPass to record commands into - */ - fun renderEdges(renderPass: RenderPass) { - val vb = edgeVertexBuffer ?: return - if (edgeIndexCount == 0) return - - renderPass.setVertexBuffer(0, vb) - // Use vanilla's sequential index buffer for lines - val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.LINES) - val indexBuffer = shapeIndexBuffer.getIndexBuffer(edgeIndexCount) - - renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) - renderPass.drawIndexed(0, 0, edgeIndexCount, 1) - } - - /** Clear all geometry data and release GPU resources. */ - fun clearData() { - faceVertexBuffer?.close() - edgeVertexBuffer?.close() - faceVertexBuffer = null - edgeVertexBuffer = null - faceIndexCount = 0 - edgeIndexCount = 0 - hasData = false - } - - /** Check if this renderer has any data to render. */ - fun hasData(): Boolean = hasData - - /** Clean up all resources. */ - fun close() { - clearData() - } - - companion object { - /** Helper to create a render pass targeting the main framebuffer. */ - fun createRenderPass(label: String): RenderPass? { - val framebuffer = mc.framebuffer ?: return null - - return RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass( - { label }, - framebuffer.colorAttachmentView, - OptionalInt.empty(), - framebuffer.depthAttachmentView, - OptionalDouble.empty() - ) - } - } + // Dedicated GPU buffers for faces and edges + private var faceVertexBuffer: GpuBuffer? = null + private var edgeVertexBuffer: GpuBuffer? = null + + // Index counts for draw calls + private var faceIndexCount = 0 + private var edgeIndexCount = 0 + + // State tracking + private var hasData = false + + /** + * Upload collected vertices from an external collector. This must be called on the main/render + * thread. + * + * @param collector The collector containing the geometry to upload + */ + fun upload(collector: RegionVertexCollector) { + val result = collector.upload() + + // Cleanup old buffers + faceVertexBuffer?.close() + edgeVertexBuffer?.close() + + // Assign new buffers and counts + faceVertexBuffer = result.faces?.buffer + faceIndexCount = result.faces?.indexCount ?: 0 + + edgeVertexBuffer = result.edges?.buffer + edgeIndexCount = result.edges?.indexCount ?: 0 + + hasData = faceVertexBuffer != null || edgeVertexBuffer != null + } + + /** + * Render faces using the given render pass. + * + * @param renderPass The active RenderPass to record commands into + */ + fun renderFaces(renderPass: RenderPass) { + val vb = faceVertexBuffer ?: return + if (faceIndexCount == 0) return + + renderPass.setVertexBuffer(0, vb) + // Use vanilla's sequential index buffer for quads + val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS) + val indexBuffer = shapeIndexBuffer.getIndexBuffer(faceIndexCount) + + renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) + renderPass.drawIndexed(0, 0, faceIndexCount, 1) + } + + /** + * Render edges using the given render pass. + * + * @param renderPass The active RenderPass to record commands into + */ + fun renderEdges(renderPass: RenderPass) { + val vb = edgeVertexBuffer ?: return + if (edgeIndexCount == 0) return + + renderPass.setVertexBuffer(0, vb) + // Use vanilla's sequential index buffer for lines + val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.LINES) + val indexBuffer = shapeIndexBuffer.getIndexBuffer(edgeIndexCount) + + renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) + renderPass.drawIndexed(0, 0, edgeIndexCount, 1) + } + + /** Clear all geometry data and release GPU resources. */ + fun clearData() { + faceVertexBuffer?.close() + edgeVertexBuffer?.close() + faceVertexBuffer = null + edgeVertexBuffer = null + faceIndexCount = 0 + edgeIndexCount = 0 + hasData = false + } + + /** Check if this renderer has any data to render. */ + fun hasData(): Boolean = hasData + + /** Clean up all resources. */ + fun close() { + clearData() + } + + companion object { + /** Helper to create a render pass targeting the main framebuffer. */ + fun createRenderPass(label: String): RenderPass? { + val framebuffer = mc.framebuffer ?: return null + return RenderSystem.getDevice() + .createCommandEncoder() + .createRenderPass( + { label }, + framebuffer.colorAttachmentView, + OptionalInt.empty(), + framebuffer.depthAttachmentView, + OptionalDouble.empty() + ) + } + } } diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt index fcd713396..0a27c058e 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt @@ -17,20 +17,25 @@ package com.lambda.graphics.mc +import com.lambda.Lambda.mc import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection import com.lambda.graphics.renderer.esp.DynamicAABB import com.lambda.module.modules.client.StyleEditor import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState -import com.lambda.util.extension.outlineShape +import com.lambda.util.extension.partialTicks import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.entity.Entity import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box +import net.minecraft.util.math.MathHelper.lerp +import net.minecraft.util.math.Vec3d import net.minecraft.util.shape.VoxelShape import java.awt.Color +import kotlin.math.min +import kotlin.math.sqrt /** * Shape builder for region-based rendering. All coordinates are automatically converted to @@ -47,12 +52,29 @@ class RegionShapeBuilder(val region: RenderRegion) { private val lineWidth: Float get() = StyleEditor.outlineWidth.toFloat() + fun box( + entity: BlockEntity, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = box(entity.pos, entity.cachedState, filled, outline, sides, mode) + + fun box( + entity: Entity, + filled: Color, + outline: Color, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) = box(entity.boundingBox, filled, outline, sides, mode) + /** Convert world coordinates to region-relative. */ - private fun toRelative(x: Double, y: Double, z: Double) = Triple( - (x - region.originX).toFloat(), - (y - region.originY).toFloat(), - (z - region.originZ).toFloat() - ) + private fun toRelative(x: Double, y: Double, z: Double) = + Triple( + (x - region.originX).toFloat(), + (y - region.originY).toFloat(), + (z - region.originZ).toFloat() + ) /** Add a colored quad face (filled rectangle). */ fun filled( @@ -113,18 +135,34 @@ class RegionShapeBuilder(val region: RenderRegion) { filled(box, color, color, sides) fun filled(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { - box.pair?.second?.let { filled(it, color, sides) } + val pair = box.pair ?: return + val prev = pair.first + val curr = pair.second + val tickDelta = mc.partialTicks + val interpolated = Box( + lerp(tickDelta, prev.minX, curr.minX), + lerp(tickDelta, prev.minY, curr.minY), + lerp(tickDelta, prev.minZ, curr.minZ), + lerp(tickDelta, prev.maxX, curr.maxX), + lerp(tickDelta, prev.maxY, curr.maxY), + lerp(tickDelta, prev.maxZ, curr.maxZ) + ) + filled(interpolated, color, sides) } - fun filled(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) = - runSafe { - val shape = outlineShape(state, pos) - if (shape.isEmpty) { - filled(Box(pos), color, sides) - } else { - filled(shape, color, sides) - } + fun filled( + pos: BlockPos, + state: BlockState, + color: Color, + sides: Int = DirectionMask.ALL + ) = runSafe { + val shape = state.getOutlineShape(world, pos) + if (shape.isEmpty) { + filled(Box(pos), color, sides) + } else { + filled(shape, color, sides) } + } fun filled(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) = runSafe { filled(pos, blockState(pos), color, sides) @@ -187,7 +225,31 @@ class RegionShapeBuilder(val region: RenderRegion) { sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And ) { - box.pair?.second?.let { outline(it, color, sides, mode) } + val pair = box.pair ?: return + val prev = pair.first + val curr = pair.second + val tickDelta = mc.partialTicks + val interpolated = Box( + lerp(tickDelta, prev.minX, curr.minX), + lerp(tickDelta, prev.minY, curr.minY), + lerp(tickDelta, prev.minZ, curr.minZ), + lerp( + tickDelta, + prev.maxX, + curr.maxX + ), + lerp( + mc.partialTicks.toDouble(), + prev.maxY, + curr.maxY + ), + lerp( + mc.partialTicks.toDouble(), + prev.maxZ, + curr.maxZ + ) + ) + outline(interpolated, color, sides, mode) } fun outline( @@ -197,7 +259,7 @@ class RegionShapeBuilder(val region: RenderRegion) { sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And ) = runSafe { - val shape = outlineShape(state, pos) + val shape = state.getOutlineShape(world, pos) if (shape.isEmpty) { outline(Box(pos), color, sides, mode) } else { @@ -295,8 +357,6 @@ class RegionShapeBuilder(val region: RenderRegion) { outline(entity.boundingBox, color, sides, mode) } - // =========== Private helpers =========== - private fun faceVertex(x: Float, y: Float, z: Float, color: Color) { collector.addFaceVertex(x, y, z, color) } @@ -327,7 +387,7 @@ class RegionShapeBuilder(val region: RenderRegion) { val dx = x2 - x1 val dy = y2 - y1 val dz = z2 - z1 - val len = kotlin.math.sqrt(dx * dx + dy * dy + dz * dz) + val len = sqrt(dx * dx + dy * dy + dz * dz) val nx = if (len > 0) dx / len else 0f val ny = if (len > 0) dy / len else 1f val nz = if (len > 0) dz / len else 0f @@ -335,4 +395,362 @@ class RegionShapeBuilder(val region: RenderRegion) { collector.addEdgeVertex(x1, y1, z1, color1, nx, ny, nz, lineWidth) collector.addEdgeVertex(x2, y2, z2, color2, nx, ny, nz, lineWidth) } + + /** + * Draw a dashed line between two world positions. + * + * @param start Start position in world coordinates + * @param end End position in world coordinates + * @param color Line color + * @param dashLength Length of each dash in blocks + * @param gapLength Length of each gap in blocks + * @param width Line width (uses default if null) + */ + fun dashedLine( + start: Vec3d, + end: Vec3d, + color: Color, + dashLength: Double = 0.5, + gapLength: Double = 0.25, + width: Float = lineWidth + ) { + val direction = end.subtract(start) + val totalLength = direction.length() + if (totalLength < 0.001) return + + val normalizedDir = direction.normalize() + var pos = 0.0 + var isDash = true + + while (pos < totalLength) { + val segmentLength = if (isDash) dashLength else gapLength + val segmentEnd = min(pos + segmentLength, totalLength) + + if (isDash) { + val segStart = start.add(normalizedDir.multiply(pos)) + val segEnd = start.add(normalizedDir.multiply(segmentEnd)) + + val (x1, y1, z1) = toRelative(segStart.x, segStart.y, segStart.z) + val (x2, y2, z2) = toRelative(segEnd.x, segEnd.y, segEnd.z) + + lineWithWidth(x1, y1, z1, x2, y2, z2, color, width) + } + + pos = segmentEnd + isDash = !isDash + } + } + + /** Draw a dashed outline for a box. */ + fun dashedOutline( + box: Box, + color: Color, + dashLength: Double = 0.5, + gapLength: Double = 0.25, + sides: Int = DirectionMask.ALL, + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + ) { + val hasEast = sides.hasDirection(DirectionMask.EAST) + val hasWest = sides.hasDirection(DirectionMask.WEST) + val hasUp = sides.hasDirection(DirectionMask.UP) + val hasDown = sides.hasDirection(DirectionMask.DOWN) + val hasSouth = sides.hasDirection(DirectionMask.SOUTH) + val hasNorth = sides.hasDirection(DirectionMask.NORTH) + + // Top edges + if (mode.check(hasUp, hasNorth)) + dashedLine( + Vec3d(box.minX, box.maxY, box.minZ), + Vec3d(box.maxX, box.maxY, box.minZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasUp, hasSouth)) + dashedLine( + Vec3d(box.minX, box.maxY, box.maxZ), + Vec3d(box.maxX, box.maxY, box.maxZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasUp, hasWest)) + dashedLine( + Vec3d(box.minX, box.maxY, box.minZ), + Vec3d(box.minX, box.maxY, box.maxZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasUp, hasEast)) + dashedLine( + Vec3d(box.maxX, box.maxY, box.maxZ), + Vec3d(box.maxX, box.maxY, box.minZ), + color, + dashLength, + gapLength + ) + + // Bottom edges + if (mode.check(hasDown, hasNorth)) + dashedLine( + Vec3d(box.minX, box.minY, box.minZ), + Vec3d(box.maxX, box.minY, box.minZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasDown, hasSouth)) + dashedLine( + Vec3d(box.minX, box.minY, box.maxZ), + Vec3d(box.maxX, box.minY, box.maxZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasDown, hasWest)) + dashedLine( + Vec3d(box.minX, box.minY, box.minZ), + Vec3d(box.minX, box.minY, box.maxZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasDown, hasEast)) + dashedLine( + Vec3d(box.maxX, box.minY, box.minZ), + Vec3d(box.maxX, box.minY, box.maxZ), + color, + dashLength, + gapLength + ) + + // Vertical edges + if (mode.check(hasWest, hasNorth)) + dashedLine( + Vec3d(box.minX, box.maxY, box.minZ), + Vec3d(box.minX, box.minY, box.minZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasNorth, hasEast)) + dashedLine( + Vec3d(box.maxX, box.maxY, box.minZ), + Vec3d(box.maxX, box.minY, box.minZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasEast, hasSouth)) + dashedLine( + Vec3d(box.maxX, box.maxY, box.maxZ), + Vec3d(box.maxX, box.minY, box.maxZ), + color, + dashLength, + gapLength + ) + if (mode.check(hasSouth, hasWest)) + dashedLine( + Vec3d(box.minX, box.maxY, box.maxZ), + Vec3d(box.minX, box.minY, box.maxZ), + color, + dashLength, + gapLength + ) + } + + /** Draw a line between two world positions. */ + fun line(start: Vec3d, end: Vec3d, color: Color, width: Float = lineWidth) { + val (x1, y1, z1) = toRelative(start.x, start.y, start.z) + val (x2, y2, z2) = toRelative(end.x, end.y, end.z) + lineWithWidth(x1, y1, z1, x2, y2, z2, color, width) + } + + /** Draw a polyline through a list of points. */ + fun polyline(points: List, color: Color, width: Float = lineWidth) { + if (points.size < 2) return + for (i in 0 until points.size - 1) { + line(points[i], points[i + 1], color, width) + } + } + + /** Draw a dashed polyline through a list of points. */ + fun dashedPolyline( + points: List, + color: Color, + dashLength: Double = 0.5, + gapLength: Double = 0.25, + width: Float = lineWidth + ) { + if (points.size < 2) return + for (i in 0 until points.size - 1) { + dashedLine(points[i], points[i + 1], color, dashLength, gapLength, width) + } + } + + /** + * Draw a quadratic Bezier curve. + * + * @param p0 Start point + * @param p1 Control point + * @param p2 End point + * @param color Line color + * @param segments Number of line segments (higher = smoother) + */ + fun quadraticBezier( + p0: Vec3d, + p1: Vec3d, + p2: Vec3d, + color: Color, + segments: Int = 16, + width: Float = lineWidth + ) { + val points = CurveUtils.quadraticBezierPoints(p0, p1, p2, segments) + polyline(points, color, width) + } + + /** + * Draw a cubic Bezier curve. + * + * @param p0 Start point + * @param p1 First control point + * @param p2 Second control point + * @param p3 End point + * @param color Line color + * @param segments Number of line segments (higher = smoother) + */ + fun cubicBezier( + p0: Vec3d, + p1: Vec3d, + p2: Vec3d, + p3: Vec3d, + color: Color, + segments: Int = 32, + width: Float = lineWidth + ) { + val points = CurveUtils.cubicBezierPoints(p0, p1, p2, p3, segments) + polyline(points, color, width) + } + + /** + * Draw a Catmull-Rom spline that passes through all control points. + * + * @param controlPoints List of points the spline should pass through (minimum 4) + * @param color Line color + * @param segmentsPerSection Segments between each pair of control points + */ + fun catmullRomSpline( + controlPoints: List, + color: Color, + segmentsPerSection: Int = 16, + width: Float = lineWidth + ) { + val points = CurveUtils.catmullRomSplinePoints(controlPoints, segmentsPerSection) + polyline(points, color, width) + } + + /** + * Draw a smooth path through waypoints using Catmull-Rom splines. Handles endpoints + * naturally by mirroring. + * + * @param waypoints List of points to pass through (minimum 2) + * @param color Line color + * @param segmentsPerSection Smoothness (higher = smoother) + */ + fun smoothPath( + waypoints: List, + color: Color, + segmentsPerSection: Int = 16, + width: Float = lineWidth + ) { + val points = CurveUtils.smoothPath(waypoints, segmentsPerSection) + polyline(points, color, width) + } + + /** Draw a dashed Bezier curve. */ + fun dashedCubicBezier( + p0: Vec3d, + p1: Vec3d, + p2: Vec3d, + p3: Vec3d, + color: Color, + segments: Int = 32, + dashLength: Double = 0.5, + gapLength: Double = 0.25, + width: Float = lineWidth + ) { + val points = CurveUtils.cubicBezierPoints(p0, p1, p2, p3, segments) + dashedPolyline(points, color, dashLength, gapLength, width) + } + + /** Draw a dashed smooth path. */ + fun dashedSmoothPath( + waypoints: List, + color: Color, + segmentsPerSection: Int = 16, + dashLength: Double = 0.5, + gapLength: Double = 0.25, + width: Float = lineWidth + ) { + val points = CurveUtils.smoothPath(waypoints, segmentsPerSection) + dashedPolyline(points, color, dashLength, gapLength, width) + } + + /** + * Draw a circle in a plane. + * + * @param center Center of the circle + * @param radius Radius of the circle + * @param normal Normal vector of the plane (determines orientation) + * @param color Line color + * @param segments Number of segments + */ + fun circle( + center: Vec3d, + radius: Double, + normal: Vec3d = Vec3d(0.0, 1.0, 0.0), + color: Color, + segments: Int = 32, + width: Float = lineWidth + ) { + // Create basis vectors perpendicular to normal + val up = + if (kotlin.math.abs(normal.y) < 0.99) Vec3d(0.0, 1.0, 0.0) + else Vec3d(1.0, 0.0, 0.0) + val u = normal.crossProduct(up).normalize() + val v = u.crossProduct(normal).normalize() + + val points = + (0..segments).map { i -> + val angle = 2.0 * Math.PI * i / segments + val x = kotlin.math.cos(angle) * radius + val y = kotlin.math.sin(angle) * radius + center.add(u.multiply(x)).add(v.multiply(y)) + } + + polyline(points, color, width) + } + + private fun lineWithWidth( + x1: Float, + y1: Float, + z1: Float, + x2: Float, + y2: Float, + z2: Float, + color: Color, + width: Float + ) { + val dx = x2 - x1 + val dy = y2 - y1 + val dz = z2 - z1 + val len = sqrt(dx * dx + dy * dy + dz * dz) + val nx = if (len > 0) dx / len else 0f + val ny = if (len > 0) dy / len else 1f + val nz = if (len > 0) dz / len else 0f + + collector.addEdgeVertex(x1, y1, z1, color, nx, ny, nz, width) + collector.addEdgeVertex(x2, y2, z2, color, nx, ny, nz, width) + } } diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt index e322816ab..e773a61fb 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt @@ -89,22 +89,19 @@ class RegionVertexCollector { * @return Pair of (faceBuffer, edgeBuffer) and their index counts, or null if no data */ fun upload(): UploadResult { - val faceResult = - if (faceVertices.isNotEmpty()) { - uploadFaces() - } else null - - val edgeResult = - if (edgeVertices.isNotEmpty()) { - uploadEdges() - } else null - - return UploadResult(faceResult, edgeResult) + val faces = uploadFaces() + val edges = uploadEdges() + return UploadResult(faces, edges) } private fun uploadFaces(): BufferResult { - // 16 bytes per vertex (3 floats + 4 bytes color) - BufferAllocator(faceVertices.size * 16).use { allocator -> + if (faceVertices.isEmpty()) return BufferResult(null, 0) + + val vertices = faceVertices.toList() + faceVertices.clear() + + var result: BufferResult? = null + BufferAllocator(vertices.size * 16).use { allocator -> val builder = BufferBuilder( allocator, @@ -112,28 +109,31 @@ class RegionVertexCollector { VertexFormats.POSITION_COLOR ) - faceVertices.forEach { v -> - builder.vertex(v.x, v.y, v.z).color(v.r, v.g, v.b, v.a) - } + vertices.forEach { v -> builder.vertex(v.x, v.y, v.z).color(v.r, v.g, v.b, v.a) } builder.endNullable()?.let { built -> val gpuDevice = RenderSystem.getDevice() - val buffer = gpuDevice.createBuffer( - { "Lambda ESP Face Buffer" }, - GpuBuffer.USAGE_VERTEX, - built.buffer - ) - val indexCount = built.drawParameters.indexCount() + val buffer = + gpuDevice.createBuffer( + { "Lambda ESP Face Buffer" }, + GpuBuffer.USAGE_VERTEX, + built.buffer + ) + result = BufferResult(buffer, built.drawParameters.indexCount()) built.close() - return BufferResult(buffer, indexCount) } } - return BufferResult(null, 0) + return result ?: BufferResult(null, 0) } private fun uploadEdges(): BufferResult { - // 32 bytes per vertex - BufferAllocator(edgeVertices.size * 32).use { allocator -> + if (edgeVertices.isEmpty()) return BufferResult(null, 0) + + val vertices = edgeVertices.toList() + edgeVertices.clear() + + var result: BufferResult? = null + BufferAllocator(vertices.size * 32).use { allocator -> val builder = BufferBuilder( allocator, @@ -141,7 +141,7 @@ class RegionVertexCollector { VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH ) - edgeVertices.forEach { v -> + vertices.forEach { v -> builder.vertex(v.x, v.y, v.z) .color(v.r, v.g, v.b, v.a) .normal(v.nx, v.ny, v.nz) @@ -150,17 +150,17 @@ class RegionVertexCollector { builder.endNullable()?.let { built -> val gpuDevice = RenderSystem.getDevice() - val buffer = gpuDevice.createBuffer( - { "Lambda ESP Edge Buffer" }, - GpuBuffer.USAGE_VERTEX, - built.buffer - ) - val indexCount = built.drawParameters.indexCount() + val buffer = + gpuDevice.createBuffer( + { "Lambda ESP Edge Buffer" }, + GpuBuffer.USAGE_VERTEX, + built.buffer + ) + result = BufferResult(buffer, built.drawParameters.indexCount()) built.close() - return BufferResult(buffer, indexCount) } } - return BufferResult(null, 0) + return result ?: BufferResult(null, 0) } data class BufferResult(val buffer: GpuBuffer?, val indexCount: Int) diff --git a/src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt b/src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt index 915c396a5..6687aa44e 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RenderRegion.kt @@ -29,32 +29,32 @@ import org.joml.Vector3f * @param originZ The Z coordinate of the region's origin */ class RenderRegion(val originX: Int, val originY: Int, val originZ: Int) { - /** - * Compute the camera-relative offset for this region. This is done in double precision to - * maintain accuracy at large coordinates. - * - * @param cameraPos The camera's world position (double precision) - * @return The offset from camera to region origin (small float, high precision) - */ - fun computeCameraRelativeOffset(cameraPos: Vec3d): Vector3f { - val offsetX = originX.toDouble() - cameraPos.x - val offsetY = originY.toDouble() - cameraPos.y - val offsetZ = originZ.toDouble() - cameraPos.z - return Vector3f(offsetX.toFloat(), offsetY.toFloat(), offsetZ.toFloat()) - } + /** + * Compute the camera-relative offset for this region. This is done in double precision to + * maintain accuracy at large coordinates. + * + * @param cameraPos The camera's world position (double precision) + * @return The offset from camera to region origin (small float, high precision) + */ + fun computeCameraRelativeOffset(cameraPos: Vec3d): Vector3f { + val offsetX = originX.toDouble() - cameraPos.x + val offsetY = originY.toDouble() - cameraPos.y + val offsetZ = originZ.toDouble() - cameraPos.z + return Vector3f(offsetX.toFloat(), offsetY.toFloat(), offsetZ.toFloat()) + } - companion object { - /** Standard size of a render region (matches Minecraft chunk size). */ - const val REGION_SIZE = 16 + companion object { + /** Standard size of a render region (matches Minecraft chunk size). */ + const val REGION_SIZE = 16 - /** - * Create a region for a chunk position. - * - * @param chunkX Chunk X coordinate - * @param chunkZ Chunk Z coordinate - * @param bottomY World bottom Y coordinate (typically -64) - */ - fun forChunk(chunkX: Int, chunkZ: Int, bottomY: Int) = - RenderRegion(chunkX * 16, bottomY, chunkZ * 16) - } + /** + * Create a region for a chunk position. + * + * @param chunkX Chunk X coordinate + * @param chunkZ Chunk Z coordinate + * @param bottomY World bottom Y coordinate (typically -64) + */ + fun forChunk(chunkX: Int, chunkZ: Int, bottomY: Int) = + RenderRegion(chunkX * 16, bottomY, chunkZ * 16) + } } diff --git a/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt b/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt index c5d62fb23..e8b0daf27 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt @@ -17,12 +17,8 @@ package com.lambda.graphics.mc -import com.lambda.Lambda.mc -import com.mojang.blaze3d.systems.RenderSystem -import org.joml.Matrix4f -import org.joml.Quaternionf -import org.joml.Vector3f -import org.joml.Vector4f +import com.lambda.graphics.esp.RegionESP +import com.lambda.graphics.esp.ShapeScope import java.util.concurrent.ConcurrentHashMap import kotlin.math.floor @@ -30,34 +26,35 @@ import kotlin.math.floor * Modern replacement for the legacy Treed system. Handles geometry that is cleared and rebuilt * every tick. Uses region-based rendering for precision. */ -class TransientRegionESP(val name: String) { - private val renderers = ConcurrentHashMap() - private val builders = ConcurrentHashMap() +class TransientRegionESP(name: String, depthTest: Boolean) : RegionESP(name, depthTest) { + private val builders = ConcurrentHashMap() /** Get or create a builder for a specific region. */ - fun getBuilder(x: Double, y: Double, z: Double): RegionShapeBuilder { - val size = RenderRegion.REGION_SIZE - val rx = (size * floor(x / size)).toInt() - val ry = (size * floor(y / size)).toInt() - val rz = (size * floor(z / size)).toInt() - - val key = (rx.toLong() and 0xFFFFFFFFL) or ((rz.toLong() and 0xFFFFFFFFL) shl 32) - - return builders.getOrPut(key) { RegionShapeBuilder(RenderRegion(rx, ry, rz)) } + override fun shapes(x: Double, y: Double, z: Double, block: ShapeScope.() -> Unit) { + val key = getRegionKey(x, y, z) + val scope = + builders.getOrPut(key) { + val size = RenderRegion.REGION_SIZE + val rx = (size * floor(x / size)).toInt() + val ry = (size * floor(y / size)).toInt() + val rz = (size * floor(z / size)).toInt() + ShapeScope(RenderRegion(rx, ry, rz)) + } + scope.apply(block) } /** Clear all current builders. Call this at the end of every tick. */ - fun clear() { + override fun clear() { builders.clear() } /** Upload collected geometry to GPU. Must be called on main thread. */ - fun upload() { + override fun upload() { val activeKeys = builders.keys().asSequence().toSet() - builders.forEach { (key, builder) -> - val renderer = renderers.getOrPut(key) { RegionRenderer(builder.region) } - renderer.upload(builder.collector) + builders.forEach { (key, scope) -> + val renderer = renderers.getOrPut(key) { RegionRenderer(scope.region) } + renderer.upload(scope.builder.collector) } renderers.forEach { (key, renderer) -> @@ -66,62 +63,4 @@ class TransientRegionESP(val name: String) { } } } - - /** Render all active regions. */ - fun render(throughWalls: Boolean = false) { - val camera = mc.gameRenderer?.camera ?: return - val cameraPos = camera.pos - - val activeRenderers = renderers.values.filter { it.hasData() } - if (activeRenderers.isEmpty()) return - - val transforms = - activeRenderers.map { renderer -> - val offset = renderer.region.computeCameraRelativeOffset(cameraPos) - val rotation = camera.rotation.conjugate(Quaternionf()) - val modelView = Matrix4f().rotation(rotation).translate(offset) - - val dynamicTransform = - RenderSystem.getDynamicUniforms() - .write( - modelView, - Vector4f(1f, 1f, 1f, 1f), - Vector3f(0f, 0f, 0f), - Matrix4f() - ) - renderer to dynamicTransform - } - - val facePass = RegionRenderer.createRenderPass("Transient ESP Faces ($name)") ?: return - facePass.use { pass -> - val pipeline = - if (throughWalls) LambdaRenderPipelines.ESP_QUADS_THROUGH - else LambdaRenderPipelines.ESP_QUADS - pass.setPipeline(pipeline) - RenderSystem.bindDefaultUniforms(pass) - transforms.forEach { (renderer, transform) -> - pass.setUniform("DynamicTransforms", transform) - renderer.renderFaces(pass) - } - } - - val edgePass = RegionRenderer.createRenderPass("Transient ESP Edges ($name)") ?: return - edgePass.use { pass -> - val pipeline = - if (throughWalls) LambdaRenderPipelines.ESP_LINES_THROUGH - else LambdaRenderPipelines.ESP_LINES - pass.setPipeline(pipeline) - RenderSystem.bindDefaultUniforms(pass) - transforms.forEach { (renderer, transform) -> - pass.setUniform("DynamicTransforms", transform) - renderer.renderEdges(pass) - } - } - } - - fun close() { - renderers.values.forEach { it.close() } - renderers.clear() - builders.clear() - } } diff --git a/src/main/kotlin/com/lambda/graphics/mc/WorldTextRenderer.kt b/src/main/kotlin/com/lambda/graphics/mc/WorldTextRenderer.kt new file mode 100644 index 000000000..9aa34034d --- /dev/null +++ b/src/main/kotlin/com/lambda/graphics/mc/WorldTextRenderer.kt @@ -0,0 +1,307 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.mc + +import com.lambda.Lambda.mc +import net.minecraft.client.font.TextRenderer +import net.minecraft.client.render.LightmapTextureManager +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.text.Text +import net.minecraft.util.math.Vec3d +import java.awt.Color + +/** + * Utility for rendering text in 3D world space. + * + * Uses Minecraft's TextRenderer to draw text that faces the camera (billboard style) at any world + * position. Handles Unicode, formatting codes, and integrates with MC's rendering system. + * + * Usage: + * ```kotlin + * // In your render event + * WorldTextRenderer.drawText( + * pos = entity.pos.add(0.0, entity.height + 0.5, 0.0), + * text = entity.name, + * color = Color.WHITE, + * scale = 0.025f + * ) + * ``` + */ +object WorldTextRenderer { + + /** Default scale for world text (MC uses 0.025f for name tags) */ + const val DEFAULT_SCALE = 0.025f + + /** Maximum light level for full brightness */ + private const val FULL_BRIGHT = LightmapTextureManager.MAX_LIGHT_COORDINATE + + /** + * Draw text at a world position, facing the camera. + * + * @param pos World position for the text + * @param text The text to render + * @param color Text color (ARGB) + * @param scale Text scale (0.025f is default name tag size) + * @param shadow Whether to draw drop shadow + * @param seeThrough Whether text should be visible through blocks + * @param centered Whether to center the text horizontally + * @param backgroundColor Background color (0 for no background) + * @param light Light level (uses full bright by default) + */ + fun drawText( + pos: Vec3d, + text: Text, + color: Color = Color.WHITE, + scale: Float = DEFAULT_SCALE, + shadow: Boolean = true, + seeThrough: Boolean = false, + centered: Boolean = true, + backgroundColor: Int = 0, + light: Int = FULL_BRIGHT + ) { + val client = mc + val camera = client.gameRenderer?.camera ?: return + val textRenderer = client.textRenderer ?: return + val immediate = client.bufferBuilders?.entityVertexConsumers ?: return + + val cameraPos = camera.pos + + val matrices = MatrixStack() + matrices.push() + + // Translate to world position relative to camera + matrices.translate(pos.x - cameraPos.x, pos.y - cameraPos.y, pos.z - cameraPos.z) + + // Billboard - face camera using camera rotation directly (same as MC's LabelCommandRenderer) + matrices.multiply(camera.rotation) + + // Scale with negative Y to flip text vertically (matches MC's 0.025, -0.025, 0.025) + matrices.scale(scale, -scale, scale) + + // Calculate text position + val textWidth = textRenderer.getWidth(text) + val x = if (centered) -textWidth / 2f else 0f + + val layerType = + if (seeThrough) TextRenderer.TextLayerType.SEE_THROUGH + else TextRenderer.TextLayerType.NORMAL + + // Draw text + textRenderer.draw( + text, + x, + 0f, + color.rgb, + shadow, + matrices.peek().positionMatrix, + immediate, + layerType, + backgroundColor, + light + ) + + matrices.pop() + + // Flush immediately for world rendering + immediate.draw() + } + + /** + * Draw text at a world position with an outline effect. + * + * @param pos World position for the text + * @param text The text to render + * @param color Text color + * @param outlineColor Outline color + * @param scale Text scale + * @param centered Whether to center the text horizontally + * @param light Light level + */ + fun drawTextWithOutline( + pos: Vec3d, + text: Text, + color: Color = Color.WHITE, + outlineColor: Color = Color.BLACK, + scale: Float = DEFAULT_SCALE, + centered: Boolean = true, + light: Int = FULL_BRIGHT + ) { + val client = mc + val camera = client.gameRenderer?.camera ?: return + val textRenderer = client.textRenderer ?: return + val immediate = client.bufferBuilders?.entityVertexConsumers ?: return + + val cameraPos = camera.pos + + val matrices = MatrixStack() + matrices.push() + + matrices.translate(pos.x - cameraPos.x, pos.y - cameraPos.y, pos.z - cameraPos.z) + + // Billboard - face camera using camera rotation directly (same as MC's LabelCommandRenderer) + matrices.multiply(camera.rotation) + matrices.scale(scale, -scale, scale) + + val textWidth = textRenderer.getWidth(text) + val x = if (centered) -textWidth / 2f else 0f + + textRenderer.drawWithOutline( + text.asOrderedText(), + x, + 0f, + color.rgb, + outlineColor.rgb, + matrices.peek().positionMatrix, + immediate, + light + ) + + matrices.pop() + immediate.draw() + } + + /** Draw a simple string at a world position. */ + fun drawString( + pos: Vec3d, + text: String, + color: Color = Color.WHITE, + scale: Float = DEFAULT_SCALE, + shadow: Boolean = true, + seeThrough: Boolean = false, + centered: Boolean = true + ) { + drawText(pos, Text.literal(text), color, scale, shadow, seeThrough, centered) + } + + /** + * Draw multiple lines of text stacked vertically. + * + * @param pos World position for the top line + * @param lines List of text lines to render + * @param color Text color + * @param scale Text scale + * @param lineSpacing Spacing between lines in scaled units (default 10) + */ + fun drawMultilineText( + pos: Vec3d, + lines: List, + color: Color = Color.WHITE, + scale: Float = DEFAULT_SCALE, + lineSpacing: Float = 10f, + shadow: Boolean = true, + seeThrough: Boolean = false, + centered: Boolean = true + ) { + val client = mc + val camera = client.gameRenderer?.camera ?: return + val textRenderer = client.textRenderer ?: return + val immediate = client.bufferBuilders?.entityVertexConsumers ?: return + + val cameraPos = camera.pos + + val matrices = MatrixStack() + matrices.push() + + matrices.translate(pos.x - cameraPos.x, pos.y - cameraPos.y, pos.z - cameraPos.z) + + // Billboard - face camera using camera rotation directly (same as MC's LabelCommandRenderer) + matrices.multiply(camera.rotation) + matrices.scale(scale, -scale, scale) + + val layerType = + if (seeThrough) TextRenderer.TextLayerType.SEE_THROUGH + else TextRenderer.TextLayerType.NORMAL + + lines.forEachIndexed { index, text -> + val textWidth = textRenderer.getWidth(text) + val x = if (centered) -textWidth / 2f else 0f + val y = index * lineSpacing + + textRenderer.draw( + text, + x, + y, + color.rgb, + shadow, + matrices.peek().positionMatrix, + immediate, + layerType, + 0, + FULL_BRIGHT + ) + } + + matrices.pop() + immediate.draw() + } + + /** + * Draw text with a background box. + * + * @param pos World position + * @param text Text to render + * @param textColor Text color + * @param backgroundColor Background color (with alpha) + * @param scale Text scale + * @param padding Padding around text in pixels + */ + fun drawTextWithBackground( + pos: Vec3d, + text: Text, + textColor: Color = Color.WHITE, + backgroundColor: Color = Color(0, 0, 0, 128), + scale: Float = DEFAULT_SCALE, + padding: Int = 2, + shadow: Boolean = false, + seeThrough: Boolean = false, + centered: Boolean = true + ) { + val client = mc + client.textRenderer ?: return + + // Calculate background color as ARGB int + val bgColorInt = + (backgroundColor.alpha shl 24) or + (backgroundColor.red shl 16) or + (backgroundColor.green shl 8) or + backgroundColor.blue + + drawText( + pos = pos, + text = text, + color = textColor, + scale = scale, + shadow = shadow, + seeThrough = seeThrough, + centered = centered, + backgroundColor = bgColorInt + ) + } + + /** Calculate the width of text in world units at a given scale. */ + fun getTextWidth(text: Text, scale: Float = DEFAULT_SCALE): Float { + val textRenderer = mc.textRenderer ?: return 0f + return textRenderer.getWidth(text) * scale + } + + /** Calculate the height of text in world units at a given scale. */ + fun getTextHeight(scale: Float = DEFAULT_SCALE): Float { + val textRenderer = mc.textRenderer ?: return 0f + return textRenderer.fontHeight * scale + } +} diff --git a/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt b/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt index ca1bd8c64..125329c1c 100644 --- a/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt +++ b/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt @@ -40,17 +40,14 @@ class ShapeBuilder(val esp: TransientRegionESP) { bottomColor: Color, topColor: Color = bottomColor, sides: Int = DirectionMask.ALL - ) = esp.getBuilder(box.minX, box.minY, box.minZ).filled(box, bottomColor, topColor, sides) + ) = Unit @ShapeDsl - fun filled(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) = - esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) - .filled(pos, state, color, sides) + fun filled(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) = Unit @ShapeDsl fun filled(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) = - esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) - .filled(pos, color, sides) + Unit @ShapeDsl fun filled( @@ -58,22 +55,13 @@ class ShapeBuilder(val esp: TransientRegionESP) { entity: BlockEntity, color: Color, sides: Int = DirectionMask.ALL - ) = - esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) - .filled(pos, entity, color, sides) + ) = Unit @ShapeDsl - fun filled(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) = - esp.getBuilder( - shape.boundingBoxes[0].minX, - shape.boundingBoxes[0].minY, - shape.boundingBoxes[0].minZ - ) - .filled(shape, color, sides) + fun filled(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) = Unit @ShapeDsl - fun filled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = - filled(box, color, color, sides) + fun filled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = Unit @ShapeDsl fun outline( @@ -82,9 +70,7 @@ class ShapeBuilder(val esp: TransientRegionESP) { topColor: Color = bottomColor, sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = - esp.getBuilder(box.minX, box.minY, box.minZ) - .outline(box, bottomColor, topColor, sides, mode) + ) = Unit @ShapeDsl fun outline( @@ -93,9 +79,7 @@ class ShapeBuilder(val esp: TransientRegionESP) { color: Color, sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = - esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) - .outline(pos, state, color, sides, mode) + ) = Unit @ShapeDsl fun outline( @@ -103,9 +87,7 @@ class ShapeBuilder(val esp: TransientRegionESP) { color: Color, sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = - esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) - .outline(pos, color, sides, mode) + ) = Unit @ShapeDsl fun outline( @@ -114,9 +96,7 @@ class ShapeBuilder(val esp: TransientRegionESP) { color: Color, sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = - esp.getBuilder(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) - .outline(pos, entity, color, sides, mode) + ) = Unit @ShapeDsl fun outline( @@ -124,13 +104,7 @@ class ShapeBuilder(val esp: TransientRegionESP) { color: Color, sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = - esp.getBuilder( - shape.boundingBoxes[0].minX, - shape.boundingBoxes[0].minY, - shape.boundingBoxes[0].minZ - ) - .outline(shape, color, sides, mode) + ) = Unit @ShapeDsl fun outline( @@ -201,9 +175,7 @@ class ShapeBuilder(val esp: TransientRegionESP) { @ShapeDsl fun filled(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { - box.pair?.second?.let { - esp.getBuilder(it.minX, it.minY, it.minZ).filled(box, color, sides) - } + Unit } @ShapeDsl @@ -213,9 +185,7 @@ class ShapeBuilder(val esp: TransientRegionESP) { sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And ) { - box.pair?.second?.let { - esp.getBuilder(it.minX, it.minY, it.minZ).outline(box, color, sides, mode) - } + Unit } @ShapeDsl @@ -226,9 +196,6 @@ class ShapeBuilder(val esp: TransientRegionESP) { sides: Int = DirectionMask.ALL, mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And ) { - box.pair?.second?.let { - esp.getBuilder(it.minX, it.minY, it.minZ) - .box(box, filledColor, outlineColor, sides, mode) - } + Unit } } diff --git a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 3c6e1ad55..fc9789b4c 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -21,8 +21,7 @@ import com.lambda.Lambda.mc import com.lambda.config.settings.collections.CollectionSetting.Companion.onDeselect import com.lambda.config.settings.collections.CollectionSetting.Companion.onSelect import com.lambda.context.SafeContext -import com.lambda.graphics.mc.ChunkedRegionESP.Companion.newChunkedRegionESP -import com.lambda.graphics.mc.RegionShapeBuilder +import com.lambda.graphics.esp.chunkedEsp import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh import com.lambda.module.Module @@ -31,10 +30,9 @@ import com.lambda.threading.runSafe import com.lambda.util.extension.blockColor import com.lambda.util.extension.getBlockState import com.lambda.util.world.toBlockPos -import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.client.render.model.BlockStateModel -import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box import java.awt.Color object BlockESP : Module( @@ -67,33 +65,33 @@ object BlockESP : Module( @JvmStatic val model: BlockStateModel get() = mc.bakedModelManager.missingModel - private val esp = newChunkedRegionESP({ true }) { world, position -> + private val esp = chunkedEsp("BlockESP") { world, position -> val state = world.getBlockState(position) - if (state.block !in blocks) return@newChunkedRegionESP + if (state.block !in blocks) return@chunkedEsp - val sides = - if (mesh) { - buildSideMesh(position) { world.getBlockState(it).block in blocks } - } else DirectionMask.ALL + val sides = if (mesh) { + buildSideMesh(position) { + world.getBlockState(it).block in blocks + } + } else DirectionMask.ALL - runSafe { build(this@newChunkedRegionESP, state, position.toBlockPos(), sides) } + runSafe { + val extractedColor = blockColor(state, position.toBlockPos()) + val pos = position.toBlockPos() + val shape = state.getOutlineShape(world, pos) + val worldBox = if (shape.isEmpty) Box(pos) else shape.boundingBox.offset(pos) + box(worldBox) { + if (drawFaces) + filled(if (useBlockColor) extractedColor else faceColor, sides) + if (drawOutlines) + outline(if (useBlockColor) extractedColor else BlockESP.outlineColor, sides, BlockESP.outlineMode) + } + } } init { onEnable { esp.rebuildAll() } - onDisable { esp.clear() } - } - - private fun SafeContext.build( - builder: RegionShapeBuilder, - state: BlockState, - pos: BlockPos, - sides: Int, - ) = with(builder) { - val blockColor = blockColor(state, pos) - - if (drawFaces) filled(pos, state, if (useBlockColor) blockColor else faceColor, sides) - if (drawOutlines) outline(pos, state, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) + onDisable { esp.close() } } private fun rebuildMesh(ctx: SafeContext, from: Any?, to: Any?): Unit = esp.rebuild() diff --git a/src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt b/src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt new file mode 100644 index 000000000..7df039dd7 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt @@ -0,0 +1,345 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.modules.render + +import com.lambda.Lambda.mc +import com.lambda.context.SafeContext +import com.lambda.event.events.GuiEvent +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.RenderMain +import com.lambda.graphics.mc.InterpolatedRegionESP +import com.lambda.graphics.mc.TransientRegionESP +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.NamedEnum +import com.lambda.util.extension.tickDelta +import com.lambda.util.math.setAlpha +import com.lambda.util.world.entitySearch +import imgui.ImGui +import net.minecraft.entity.Entity +import net.minecraft.entity.ItemEntity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.decoration.ArmorStandEntity +import net.minecraft.entity.decoration.EndCrystalEntity +import net.minecraft.entity.mob.HostileEntity +import net.minecraft.entity.mob.MobEntity +import net.minecraft.entity.passive.AnimalEntity +import net.minecraft.entity.passive.PassiveEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.entity.projectile.ProjectileEntity +import net.minecraft.entity.vehicle.AbstractMinecartEntity +import net.minecraft.entity.vehicle.BoatEntity +import net.minecraft.util.math.Vec3d +import java.awt.Color + +object EntityESP : Module( + name = "EntityESP", + description = "Highlight entities with smooth interpolated rendering", + tag = ModuleTag.RENDER +) { + private val esp = InterpolatedRegionESP("EntityESP") + + private data class LabelData( + val screenX: Float, + val screenY: Float, + val text: String, + val color: Color, + val scale: Float + ) + + private val pendingLabels = mutableListOf() + + private val range by setting("Range", 64.0, 8.0..256.0, 1.0, "Maximum render distance").group(Group.General) + private val throughWalls by setting("Through Walls", true, "Render through blocks").group(Group.General) + private val self by setting("Self", false, "Render own player in third person").group(Group.General) + + private val players by setting("Players", true, "Highlight players").group(Group.Entities) + private val hostiles by setting("Hostiles", true, "Highlight hostile mobs").group(Group.Entities) + private val passives by setting("Passives", false, "Highlight passive mobs (animals)").group(Group.Entities) + private val neutrals by setting("Neutrals", false, "Highlight neutral mobs").group(Group.Entities) + private val items by setting("Items", false, "Highlight dropped items").group(Group.Entities) + private val projectiles by setting("Projectiles", false, "Highlight projectiles").group(Group.Entities) + private val vehicles by setting("Vehicles", false, "Highlight boats and minecarts").group(Group.Entities) + private val crystals by setting("Crystals", true, "Highlight end crystals").group(Group.Entities) + private val armorStands by setting("Armor Stands", false, "Highlight armor stands").group(Group.Entities) + + private val drawBoxes by setting("Boxes", true, "Draw entity boxes").group(Group.Render) + private val drawFilled by setting("Filled", true, "Fill entity boxes") { drawBoxes }.group(Group.Render) + private val drawOutline by setting("Outline", true, "Draw box outlines") { drawBoxes }.group(Group.Render) + private val filledAlpha by setting("Filled Alpha", 0.2, 0.0..1.0, 0.05) { drawBoxes && drawFilled }.group(Group.Render) + private val outlineAlpha by setting("Outline Alpha", 0.8, 0.0..1.0, 0.05) { drawBoxes && drawOutline }.group(Group.Render) + + private val tracers by setting("Tracers", true, "Draw lines to entities").group(Group.Tracers) + private val tracerOrigin by setting("Tracer Origin", TracerOrigin.Eyes, "Where tracers start from") { tracers }.group(Group.Tracers) + private val tracerWidth by setting("Tracer Width", 1.5f, 0.5f..5f, 0.5f) { tracers }.group(Group.Tracers) + private val dashedTracers by setting("Dashed Tracers", false, "Use dashed lines for tracers") { tracers }.group(Group.Tracers) + private val dashLength by setting("Dash Length", 1.0, 0.25..2.0, 0.25) { tracers && dashedTracers }.group(Group.Tracers) + private val gapLength by setting("Gap Length", 0.5, 0.1..1.0, 0.1) { tracers && dashedTracers }.group(Group.Tracers) + + private val nameTags by setting("Name Tags", false, "Show entity name tags").group(Group.NameTags) + private val nameTagDistance by setting("Show Distance", true, "Show distance in name tags") { nameTags }.group(Group.NameTags) + private val nameTagHealth by setting("Show Health", true, "Show health in name tags") { nameTags }.group(Group.NameTags) + private val nameTagBackground by setting("Name Background", true, "Draw background behind name tags") { nameTags }.group(Group.NameTags) + + private val playerColor by setting("Player Color", Color(255, 50, 50), "Color for players").group(Group.Colors) + private val hostileColor by setting("Hostile Color", Color(255, 100, 0), "Color for hostile mobs").group(Group.Colors) + private val passiveColor by setting("Passive Color", Color(50, 255, 50), "Color for passive mobs").group(Group.Colors) + private val neutralColor by setting("Neutral Color", Color(255, 255, 50), "Color for neutral mobs").group(Group.Colors) + private val itemColor by setting("Item Color", Color(100, 100, 255), "Color for items").group(Group.Colors) + private val projectileColor by setting("Projectile Color", Color(200, 200, 200), "Color for projectiles").group(Group.Colors) + private val vehicleColor by setting("Vehicle Color", Color(150, 100, 50), "Color for vehicles").group(Group.Colors) + private val crystalColor by setting("Crystal Color", Color(255, 0, 255), "Color for end crystals").group(Group.Colors) + private val otherColor by setting("Other Color", Color(200, 200, 200), "Color for other entities").group(Group.Colors) + + init { + listen { + esp.tick() + + entitySearch(range) { shouldRender(it) }.forEach { entity -> + val color = getEntityColor(entity) + val box = entity.boundingBox + + esp.shapes(entity.x, entity.y, entity.z) { + if (drawBoxes) { + box(box, entity.id) { + if (drawFilled) + filled(color.setAlpha(filledAlpha)) + if (drawOutline) + outline( + color.setAlpha(outlineAlpha) + ) + } + } + } + } + + esp.upload() + } + + listen { + val tickDelta = mc.tickDelta + esp.render(tickDelta) + + // Clear pending labels from previous frame + pendingLabels.clear() + + if (tracers || nameTags) { + val tracerEsp = TransientRegionESP( + "EntityESP-Tracers", + depthTest = !throughWalls + ) + entitySearch(range) { shouldRender(it) }.forEach { entity -> + val color = getEntityColor(entity) + val entityPos = getInterpolatedPos(entity, tickDelta) + + if (tracers) { + val startPos = getTracerStartPos(tickDelta) + val endPos = entityPos.add(0.0, entity.height / 2.0, 0.0) + + tracerEsp.shapes(entity.x, entity.y, entity.z) { + tracer(startPos, endPos, entity.id) { + color(color.setAlpha(outlineAlpha)) + width(tracerWidth) + if (dashedTracers) dashed(dashLength, gapLength) + } + } + } + + if (nameTags) { + val namePos = entityPos.add(0.0, entity.height + 0.3, 0.0) + // Project to screen coords NOW while matrices are + // valid + val screen = RenderMain.worldToScreen(namePos) + if (screen != null) { + val nameText = buildNameTag(entity) + // Calculate distance-based scale (closer = + // larger) + val distance = player.pos.distanceTo(namePos).toFloat() + val scale = (1.0f / (distance * 0.1f + 1f)).coerceIn(0.5f, 2.0f) + pendingLabels.add( + LabelData( + screen.x, + screen.y, + nameText, + color, + scale + ) + ) + } + } + } + + tracerEsp.upload() + tracerEsp.render() + tracerEsp.close() + } + } + + // Draw ImGUI labels using pre-computed screen coordinates + listen { + val drawList = ImGui.getBackgroundDrawList() + val font = ImGui.getFont() + + pendingLabels.forEach { label -> + val fontSize = (font.fontSize * label.scale).toInt() + val textSize = imgui.ImVec2() + ImGui.calcTextSize(textSize, label.text) + + // Scale text size based on our custom scale + val tw = textSize.x * label.scale + val th = textSize.y * label.scale + + // Center text horizontally + val x = label.screenX - tw / 2f + val y = label.screenY + + // Color conversion (ABGR for ImGui) + val textColor = + (label.color.alpha shl 24) or + (label.color.blue shl 16) or + (label.color.green shl 8) or + label.color.red + val shadowColor = (200 shl 24) or 0 // Black with alpha + + if (nameTagBackground) { + val bgColor = (160 shl 24) or 0 // Black with 160 alpha + val padX = 4f * label.scale + val padY = 2f * label.scale + drawList.addRectFilled( + x - padX, + y - padY, + x + tw + padX, + y + th + padY, + bgColor, + 3f * label.scale + ) + } else { + // Shadow + drawList.addText( + font, + fontSize, + imgui.ImVec2( + x + 1f * label.scale, + y + 1f * label.scale + ), + shadowColor, + label.text + ) + } + drawList.addText( + font, + fontSize, + imgui.ImVec2(x, y), + textColor, + label.text + ) + } + } + + onDisable { esp.close() } + } + + private fun SafeContext.shouldRender(entity: Entity): Boolean { + if (entity == player && !self) return false + if (entity is LivingEntity && !entity.isAlive) return false + return when (entity) { + is PlayerEntity -> players + is HostileEntity -> hostiles + is AnimalEntity -> passives + is PassiveEntity -> passives + is MobEntity -> neutrals + is ItemEntity -> items + is ProjectileEntity -> projectiles + is BoatEntity -> vehicles + is AbstractMinecartEntity -> vehicles + is EndCrystalEntity -> crystals + is ArmorStandEntity -> armorStands + else -> false + } + } + + private fun getEntityColor(entity: Entity): Color { + return when (entity) { + is PlayerEntity -> playerColor + is HostileEntity -> hostileColor + is AnimalEntity -> passiveColor + is PassiveEntity -> passiveColor + is MobEntity -> neutralColor + is ItemEntity -> itemColor + is ProjectileEntity -> projectileColor + is BoatEntity -> vehicleColor + is AbstractMinecartEntity -> vehicleColor + is EndCrystalEntity -> crystalColor + else -> otherColor + } + } + + private fun getInterpolatedPos(entity: Entity, tickDelta: Float): Vec3d { + val x = entity.lastRenderX + (entity.x - entity.lastRenderX) * tickDelta + val y = entity.lastRenderY + (entity.y - entity.lastRenderY) * tickDelta + val z = entity.lastRenderZ + (entity.z - entity.lastRenderZ) * tickDelta + return Vec3d(x, y, z) + } + + private fun SafeContext.getTracerStartPos(tickDelta: Float): Vec3d { + val playerPos = getInterpolatedPos(player, tickDelta) + + return when (tracerOrigin) { + TracerOrigin.Feet -> playerPos + TracerOrigin.Center -> playerPos.add(0.0, player.height / 2.0, 0.0) + TracerOrigin.Eyes -> + playerPos.add(0.0, player.standingEyeHeight.toDouble(), 0.0) + TracerOrigin.Crosshair -> { + val camera = mc.gameRenderer?.camera ?: return playerPos + camera.pos.add(Vec3d(camera.horizontalPlane).multiply(0.1)) + } + } + } + + private fun SafeContext.buildNameTag(entity: Entity): String { + val builder = StringBuilder() + val name = entity.displayName?.string ?: entity.type.name.string + builder.append(name) + if (nameTagHealth && entity is LivingEntity) { + builder.append(" [${entity.health.toInt()}/${entity.maxHealth.toInt()}]") + } + if (nameTagDistance) { + val dist = player.distanceTo(entity).toInt() + builder.append(" ${dist}m") + } + return builder.toString() + } + + enum class TracerOrigin(override val displayName: String) : NamedEnum { + Feet("Feet"), + Center("Center"), + Eyes("Eyes"), + Crosshair("Crosshair") + } + + private enum class Group(override val displayName: String) : NamedEnum { + General("General"), + Entities("Entities"), + Render("Render"), + Tracers("Tracers"), + NameTags("Name Tags"), + Colors("Colors") + } +} diff --git a/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt index 910684cc9..5ac2dd143 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -18,19 +18,15 @@ package com.lambda.module.modules.render import com.lambda.context.SafeContext -import com.lambda.event.events.onStaticRender +import com.lambda.graphics.esp.ShapeScope import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh -import com.lambda.graphics.renderer.esp.ShapeBuilder import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.util.NamedEnum import com.lambda.util.extension.blockColor -import com.lambda.util.extension.outlineShape import com.lambda.util.math.setAlpha -import com.lambda.util.world.blockEntitySearch -import com.lambda.util.world.entitySearch import net.minecraft.block.entity.BarrelBlockEntity import net.minecraft.block.entity.BlastFurnaceBlockEntity import net.minecraft.block.entity.BlockEntity @@ -50,145 +46,142 @@ import net.minecraft.entity.vehicle.MinecartEntity import java.awt.Color object StorageESP : Module( - name = "StorageESP", - description = "Render storage blocks/entities", - tag = ModuleTag.RENDER, + name = "StorageESP", + description = "Render storage blocks/entities", + tag = ModuleTag.RENDER, ) { - /* General settings */ - private val distance by setting("Distance", 64.0, 10.0..256.0, 1.0, "Maximum distance for rendering").group(Group.General) - - /* Render settings */ - private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks").onValueChange { _, to -> drawEdges = !to && !drawFaces }.group(Group.Render) - private var drawEdges: Boolean by setting("Draw Edges", true, "Draw edges of blocks").onValueChange { _, to -> drawFaces = !to && !drawEdges }.group(Group.Render) - private val mode by setting("Outline Mode", DirectionMask.OutlineMode.And, "Outline mode").group(Group.Render) - private val mesh by setting("Mesh", true, "Connect similar adjacent blocks").group(Group.Render) - - /* Color settings */ - private val useBlockColor by setting("Use Block Color", true, "Use the color of the block instead").group(Group.Color) - private val facesAlpha by setting("Faces Alpha", 0.3, 0.1..1.0, 0.05).group(Group.Color) - private val edgesAlpha by setting("Edges Alpha", 0.3, 0.1..1.0, 0.05).group(Group.Color) - - // TODO: - // val blockColors by setting("Block Colors", mapOf()) { page == Page.Color && !useBlockColor } - // val renders by setting("Render Blocks", mapOf()) { page == Page.General } - // - // TODO: Create enum of MapColors - - // I used this to extract the colors as rgb format - //> function extract(color) { - // ... console.log((color >> 16) & 0xFF) - // ... console.log((color >> 8) & 0xFF) - // ... console.log(color & 0xFF) - // ... } - - private val barrelColor by setting("Barrel Color", Color(143, 119, 72)) { !useBlockColor }.group(Group.Color) - private val blastFurnaceColor by setting("Blast Furnace Color", Color(153, 153, 153)) { !useBlockColor }.group(Group.Color) - private val brewingStandColor by setting("Brewing Stand Color", Color(167, 167, 167)) { !useBlockColor }.group(Group.Color) - private val trappedChestColor by setting("Trapped Chest Color", Color(216, 127, 51)) { !useBlockColor }.group(Group.Color) - private val chestColor by setting("Chest Color", Color(216, 127, 51)) { !useBlockColor }.group(Group.Color) - private val dispenserColor by setting("Dispenser Color", Color(153, 153, 153)) { !useBlockColor }.group(Group.Color) - private val enderChestColor by setting("Ender Chest Color", Color(127, 63, 178)) { !useBlockColor }.group(Group.Color) - private val furnaceColor by setting("Furnace Color", Color(153, 153, 153)) { !useBlockColor }.group(Group.Color) - private val hopperColor by setting("Hopper Color", Color(76, 76, 76)) { !useBlockColor }.group(Group.Color) - private val smokerColor by setting("Smoker Color", Color(112, 112, 112)) { !useBlockColor }.group(Group.Color) - private val shulkerColor by setting("Shulker Color", Color(178, 76, 216)) { !useBlockColor }.group(Group.Color) - private val itemFrameColor by setting("Item Frame Color", Color(216, 127, 51)) { !useBlockColor }.group(Group.Color) - private val cartColor by setting("Minecart Color", Color(102, 127, 51)) { !useBlockColor }.group(Group.Color) - - private val entities = setOf( - BarrelBlockEntity::class, - BlastFurnaceBlockEntity::class, - BrewingStandBlockEntity::class, - TrappedChestBlockEntity::class, - ChestBlockEntity::class, - DispenserBlockEntity::class, - EnderChestBlockEntity::class, - FurnaceBlockEntity::class, - HopperBlockEntity::class, - SmokerBlockEntity::class, - ShulkerBoxBlockEntity::class, - AbstractMinecartEntity::class, - ItemFrameEntity::class, - MinecartEntity::class, - ) - - init { - onStaticRender { builder -> - blockEntitySearch(distance) - .filter { it::class in entities } - .forEach { with(builder) { build(it, excludedSides(it)) } } - - val mineCarts = entitySearch(distance) - val itemFrames = entitySearch(distance) - (mineCarts + itemFrames) - .forEach { with(builder) { build(it, DirectionMask.ALL) } } // FixMe: Exclude entity shape sides - } - } - - private fun SafeContext.excludedSides(blockEntity: BlockEntity): Int { - val isFullCube = blockEntity.cachedState.isFullCube(world, blockEntity.pos) - return if (mesh && isFullCube) { - buildSideMesh(blockEntity.pos) { neighbor -> - val other = world.getBlockEntity(neighbor) ?: return@buildSideMesh false - val otherFullCube = other.cachedState.isFullCube(world, other.pos) - val sameType = blockEntity.cachedState.block == other.cachedState.block - val searchedFor = other::class in entities - - searchedFor && otherFullCube && sameType - } - } else DirectionMask.ALL - } - - private fun ShapeBuilder.build( - block: BlockEntity, - sides: Int, - ) = runSafe { - val color = - if (useBlockColor) blockColor(block.cachedState, block.pos) - else block.color ?: return@runSafe - - val shape = outlineShape(block.cachedState, block.pos) - - if (drawFaces) filled(shape, color.setAlpha(facesAlpha), sides) - if (drawEdges) outline(shape, color.setAlpha(edgesAlpha), sides, mode) - } - - private fun ShapeBuilder.build( - entity: Entity, - sides: Int, - ) = runSafe { - val color = entity.color ?: return@runSafe - - if (drawFaces) filled(entity.boundingBox, color.setAlpha(facesAlpha), sides) - if (drawEdges) outline(entity.boundingBox, color.setAlpha(edgesAlpha), sides, mode) - } - - private val BlockEntity?.color get() = - when (this) { - is BarrelBlockEntity -> barrelColor - is BlastFurnaceBlockEntity -> blastFurnaceColor - is BrewingStandBlockEntity -> brewingStandColor - is TrappedChestBlockEntity -> trappedChestColor - is ChestBlockEntity -> chestColor - is DispenserBlockEntity -> dispenserColor - is EnderChestBlockEntity -> enderChestColor - is FurnaceBlockEntity -> furnaceColor - is HopperBlockEntity -> hopperColor - is SmokerBlockEntity -> smokerColor - is ShulkerBoxBlockEntity -> shulkerColor - else -> null - } - - private val Entity?.color get() = - when (this) { - is AbstractMinecartEntity -> cartColor - is ItemFrameEntity -> itemFrameColor - else -> null - } - - private enum class Group(override val displayName: String) : NamedEnum { - General("General"), - Render("Render"), - Color("Color") - } + private val distance by setting("Distance", 64.0, 10.0..256.0, 1.0, "Maximum distance for rendering").group(Group.General) + private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks").group(Group.Render) + private var drawEdges: Boolean by setting("Draw Edges", true, "Draw edges of blocks").group(Group.Render) + private val mode by setting("Outline Mode", DirectionMask.OutlineMode.And, "Outline mode").group(Group.Render) + private val mesh by setting("Mesh", true, "Connect similar adjacent blocks").group(Group.Render) + private val useBlockColor by setting("Use Block Color", true, "Use the color of the block instead").group(Group.Color) + private val facesAlpha by setting("Faces Alpha", 0.3, 0.1..1.0, 0.05).group(Group.Color) + private val edgesAlpha by setting("Edges Alpha", 0.3, 0.1..1.0, 0.05).group(Group.Color) + + // TODO: + // val blockColors by setting("Block Colors", mapOf()) { page == Page.Color + // && + // !useBlockColor } + // val renders by setting("Render Blocks", mapOf()) { page == Page.General + // } + // + // TODO: Create enum of MapColors + + // I used this to extract the colors as rgb format + // > function extract(color) { + // ... console.log((color >> 16) & 0xFF) + // ... console.log((color >> 8) & 0xFF) + // ... console.log(color & 0xFF) + // ... } + + private val barrelColor by setting("Barrel Color", Color(143, 119, 72)) { !useBlockColor }.group(Group.Color) + private val blastFurnaceColor by setting("Blast Furnace Color", Color(153, 153, 153)) { !useBlockColor }.group(Group.Color) + private val brewingStandColor by setting("Brewing Stand Color", Color(167, 167, 167)) { !useBlockColor }.group(Group.Color) + private val trappedChestColor by setting("Trapped Chest Color", Color(216, 127, 51)) { !useBlockColor }.group(Group.Color) + private val chestColor by setting("Chest Color", Color(216, 127, 51)) { !useBlockColor }.group(Group.Color) + private val dispenserColor by setting("Dispenser Color", Color(153, 153, 153)) { !useBlockColor }.group(Group.Color) + private val enderChestColor by setting("Ender Chest Color", Color(127, 63, 178)) { !useBlockColor }.group(Group.Color) + private val furnaceColor by setting("Furnace Color", Color(153, 153, 153)) { !useBlockColor }.group(Group.Color) + private val hopperColor by setting("Hopper Color", Color(76, 76, 76)) { !useBlockColor }.group(Group.Color) + private val smokerColor by setting("Smoker Color", Color(112, 112, 112)) { !useBlockColor }.group(Group.Color) + private val shulkerColor by setting("Shulker Color", Color(178, 76, 216)) { !useBlockColor }.group(Group.Color) + private val itemFrameColor by setting("Item Frame Color", Color(216, 127, 51)) { !useBlockColor }.group(Group.Color) + private val cartColor by setting("Minecart Color", Color(102, 127, 51)) { !useBlockColor }.group(Group.Color) + + private val entities = setOf( + BarrelBlockEntity::class, + BlastFurnaceBlockEntity::class, + BrewingStandBlockEntity::class, + TrappedChestBlockEntity::class, + ChestBlockEntity::class, + DispenserBlockEntity::class, + EnderChestBlockEntity::class, + FurnaceBlockEntity::class, + HopperBlockEntity::class, + SmokerBlockEntity::class, + ShulkerBoxBlockEntity::class, + AbstractMinecartEntity::class, + ItemFrameEntity::class, + MinecartEntity::class, + ) + + init { + // onStaticRender { render -> + // blockEntitySearch(distance) + // .filter { it::class in entities } + // .forEach { render.shapes(it.pos) { build(it, excludedSides(it)) } } + // + // val mineCarts = + // entitySearch(distance).filter { + // it::class in entities + // } + // val itemFrames = + // entitySearch(distance).filter { + // it::class in entities + // } + // (mineCarts + itemFrames).forEach { + // render.shapes(it.blockPos) { build(it, DirectionMask.ALL) } + // } + // } + } + + private fun SafeContext.excludedSides(blockEntity: BlockEntity): Int { + val isFullCube = blockEntity.cachedState.isFullCube(world, blockEntity.pos) + return if (mesh && isFullCube) { + buildSideMesh(blockEntity.pos) { neighbor -> + val other = + world.getBlockEntity(neighbor) ?: return@buildSideMesh false + val otherFullCube = other.cachedState.isFullCube(world, other.pos) + val sameType = + blockEntity.cachedState.block == other.cachedState.block + val searchedFor = other::class in entities + + searchedFor && otherFullCube && sameType + } + } else DirectionMask.ALL + } + + private fun ShapeScope.build(block: BlockEntity, sides: Int) = runSafe { + val color = + if (useBlockColor) blockColor(block.cachedState, block.pos) + else block.color ?: return@runSafe + box(block, color.setAlpha(facesAlpha), color.setAlpha(edgesAlpha), sides, mode) + } + + private fun ShapeScope.build(entity: Entity, sides: Int) = runSafe { + val color = entity.color ?: return@runSafe + box(entity, color.setAlpha(facesAlpha), color.setAlpha(edgesAlpha), sides, mode) + } + + private val BlockEntity?.color + get() = + when (this) { + is BarrelBlockEntity -> barrelColor + is BlastFurnaceBlockEntity -> blastFurnaceColor + is BrewingStandBlockEntity -> brewingStandColor + is TrappedChestBlockEntity -> trappedChestColor + is ChestBlockEntity -> chestColor + is DispenserBlockEntity -> dispenserColor + is EnderChestBlockEntity -> enderChestColor + is FurnaceBlockEntity -> furnaceColor + is HopperBlockEntity -> hopperColor + is SmokerBlockEntity -> smokerColor + is ShulkerBoxBlockEntity -> shulkerColor + else -> null + } + + private val Entity?.color + get() = + when (this) { + is AbstractMinecartEntity -> cartColor + is ItemFrameEntity -> itemFrameColor + else -> null + } + + private enum class Group(override val displayName: String) : NamedEnum { + General("General"), + Render("Render"), + Color("Color") + } } From 8e28a6d21d2e79856bf3e88108213867974c1ad6 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Dec 2025 01:52:30 +0100 Subject: [PATCH 15/20] Migrated to new esp shape builder --- .../com/lambda/config/AutomationConfig.kt | 5 +- .../com/lambda/event/events/RenderEvent.kt | 10 +- .../kotlin/com/lambda/graphics/RenderMain.kt | 4 +- .../lambda/graphics/mc/RegionShapeBuilder.kt | 24 +-- .../lambda/graphics/mc/TransientRegionESP.kt | 2 +- .../lambda/graphics/renderer/esp/ShapeDsl.kt | 201 ------------------ .../construction/simulation/Simulation.kt | 9 +- .../simulation/context/BreakContext.kt | 13 +- .../simulation/context/InteractContext.kt | 19 +- .../simulation/result/Drawable.kt | 4 +- .../simulation/result/results/BreakResult.kt | 41 ++-- .../result/results/GenericResult.kt | 51 +++-- .../result/results/InteractResult.kt | 39 ++-- .../simulation/result/results/PreSimResult.kt | 33 ++- .../managers/breaking/BreakManager.kt | 25 ++- .../lambda/module/modules/debug/BlockTest.kt | 8 +- .../lambda/module/modules/debug/RenderTest.kt | 12 +- .../module/modules/movement/BackTrack.kt | 7 +- .../lambda/module/modules/movement/Blink.kt | 8 +- .../lambda/module/modules/player/AirPlace.kt | 8 +- .../module/modules/player/PacketMine.kt | 14 +- .../module/modules/player/WorldEater.kt | 6 +- .../module/modules/render/StorageESP.kt | 43 ++-- 23 files changed, 223 insertions(+), 363 deletions(-) delete mode 100644 src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt diff --git a/src/main/kotlin/com/lambda/config/AutomationConfig.kt b/src/main/kotlin/com/lambda/config/AutomationConfig.kt index 56e986838..ccf2a297d 100644 --- a/src/main/kotlin/com/lambda/config/AutomationConfig.kt +++ b/src/main/kotlin/com/lambda/config/AutomationConfig.kt @@ -85,9 +85,8 @@ open class AutomationConfig( var drawables = listOf() init { - onStaticRender { - if (renders) - with(it) { drawables.forEach { with(it) { buildRenderer() } } } + onStaticRender { esp -> + drawables.forEach { it.render(esp) } } } } diff --git a/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/src/main/kotlin/com/lambda/event/events/RenderEvent.kt index 20a4b516d..36e2dbe37 100644 --- a/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -23,13 +23,13 @@ import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.RenderMain -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.mc.TransientRegionESP -fun Any.onStaticRender(block: SafeContext.(ShapeBuilder) -> Unit) = - listen { block(ShapeBuilder(RenderMain.StaticESP)) } +fun Any.onStaticRender(block: SafeContext.(TransientRegionESP) -> Unit) = + listen { block(RenderMain.StaticESP) } -fun Any.onDynamicRender(block: SafeContext.(ShapeBuilder) -> Unit) = - listen { block(ShapeBuilder(RenderMain.DynamicESP)) } +fun Any.onDynamicRender(block: SafeContext.(TransientRegionESP) -> Unit) = + listen { block(RenderMain.DynamicESP) } sealed class RenderEvent { object Upload : Event diff --git a/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 2a1ac695f..1353fec6e 100644 --- a/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -32,10 +32,10 @@ import org.joml.Vector4f object RenderMain { @JvmStatic - val StaticESP = TransientRegionESP("Static", true) + val StaticESP = TransientRegionESP("Static") @JvmStatic - val DynamicESP = TransientRegionESP("Dynamic", false) + val DynamicESP = TransientRegionESP("Dynamic") val projectionMatrix = Matrix4f() val modelViewMatrix diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt index 0a27c058e..f437b7012 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt @@ -41,7 +41,7 @@ import kotlin.math.sqrt * Shape builder for region-based rendering. All coordinates are automatically converted to * region-relative positions. * - * This class provides the same DSL as ShapeDsl but collects vertex data in thread-safe collections + * This class provides drawing primitives for region-based rendering and collects vertex data in thread-safe collections * for later upload to MC's BufferBuilder. * * @param region The render region (provides origin for coordinate conversion) @@ -160,7 +160,7 @@ class RegionShapeBuilder(val region: RenderRegion) { if (shape.isEmpty) { filled(Box(pos), color, sides) } else { - filled(shape, color, sides) + filled(shape.offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()), color, sides) } } @@ -233,21 +233,9 @@ class RegionShapeBuilder(val region: RenderRegion) { lerp(tickDelta, prev.minX, curr.minX), lerp(tickDelta, prev.minY, curr.minY), lerp(tickDelta, prev.minZ, curr.minZ), - lerp( - tickDelta, - prev.maxX, - curr.maxX - ), - lerp( - mc.partialTicks.toDouble(), - prev.maxY, - curr.maxY - ), - lerp( - mc.partialTicks.toDouble(), - prev.maxZ, - curr.maxZ - ) + lerp(tickDelta, prev.maxX, curr.maxX), + lerp(tickDelta, prev.maxY, curr.maxY), + lerp(tickDelta, prev.maxZ, curr.maxZ) ) outline(interpolated, color, sides, mode) } @@ -263,7 +251,7 @@ class RegionShapeBuilder(val region: RenderRegion) { if (shape.isEmpty) { outline(Box(pos), color, sides, mode) } else { - outline(shape, color, sides, mode) + outline(shape.offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()), color, sides, mode) } } diff --git a/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt b/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt index e8b0daf27..bc1cd812c 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/TransientRegionESP.kt @@ -26,7 +26,7 @@ import kotlin.math.floor * Modern replacement for the legacy Treed system. Handles geometry that is cleared and rebuilt * every tick. Uses region-based rendering for precision. */ -class TransientRegionESP(name: String, depthTest: Boolean) : RegionESP(name, depthTest) { +class TransientRegionESP(name: String, depthTest: Boolean = false) : RegionESP(name, depthTest) { private val builders = ConcurrentHashMap() /** Get or create a builder for a specific region. */ diff --git a/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt b/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt deleted file mode 100644 index 125329c1c..000000000 --- a/src/main/kotlin/com/lambda/graphics/renderer/esp/ShapeDsl.kt +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.renderer.esp - -import com.lambda.graphics.mc.TransientRegionESP -import net.minecraft.block.BlockState -import net.minecraft.block.entity.BlockEntity -import net.minecraft.entity.Entity -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Box -import net.minecraft.util.shape.VoxelShape -import java.awt.Color - -@DslMarker -annotation class ShapeDsl - -/** - * Bridge class that provides the legacy ShapeBuilder API while writing to the new - * TransientRegionESP. - */ -class ShapeBuilder(val esp: TransientRegionESP) { - @ShapeDsl - fun filled( - box: Box, - bottomColor: Color, - topColor: Color = bottomColor, - sides: Int = DirectionMask.ALL - ) = Unit - - @ShapeDsl - fun filled(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) = Unit - - @ShapeDsl - fun filled(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) = - Unit - - @ShapeDsl - fun filled( - pos: BlockPos, - entity: BlockEntity, - color: Color, - sides: Int = DirectionMask.ALL - ) = Unit - - @ShapeDsl - fun filled(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) = Unit - - @ShapeDsl - fun filled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = Unit - - @ShapeDsl - fun outline( - box: Box, - bottomColor: Color, - topColor: Color = bottomColor, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = Unit - - @ShapeDsl - fun outline( - pos: BlockPos, - state: BlockState, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = Unit - - @ShapeDsl - fun outline( - pos: BlockPos, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = Unit - - @ShapeDsl - fun outline( - pos: BlockPos, - entity: BlockEntity, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = Unit - - @ShapeDsl - fun outline( - shape: VoxelShape, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = Unit - - @ShapeDsl - fun outline( - box: Box, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) = outline(box, color, color, sides, mode) - - @ShapeDsl - fun box( - pos: BlockPos, - state: BlockState, - filled: Color, - outline: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) { - filled(pos, state, filled, sides) - outline(pos, state, outline, sides, mode) - } - - @ShapeDsl - fun box( - pos: BlockPos, - filled: Color, - outline: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) { - filled(pos, filled, sides) - outline(pos, outline, sides, mode) - } - - @ShapeDsl - fun box( - box: Box, - filled: Color, - outline: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) { - filled(box, filled, sides) - outline(box, outline, sides, mode) - } - - @ShapeDsl - fun box( - entity: BlockEntity, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) { - filled(entity.pos, entity, color, sides) - outline(entity.pos, entity, color, sides, mode) - } - - @ShapeDsl - fun box( - entity: Entity, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, - ) { - filled(entity.boundingBox, color, sides) - outline(entity.boundingBox, color, sides, mode) - } - - @ShapeDsl - fun filled(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { - Unit - } - - @ShapeDsl - fun outline( - box: DynamicAABB, - color: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And - ) { - Unit - } - - @ShapeDsl - fun box( - box: DynamicAABB, - filledColor: Color, - outlineColor: Color, - sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And - ) { - Unit - } -} diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt index cef79a426..7785c578e 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/Simulation.kt @@ -19,7 +19,8 @@ package com.lambda.interaction.construction.simulation import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.esp.ShapeScope +import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.construction.blueprint.Blueprint import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.Drawable @@ -63,8 +64,10 @@ data class Simulation( .map { PossiblePos(it.key.toBlockPos(), it.value.count { it.rank.ordinal < 4 }) } class PossiblePos(val pos: BlockPos, val interactions: Int) : Drawable { - override fun ShapeBuilder.buildRenderer() { - box(Vec3d.ofBottomCenter(pos).playerBox(), Color(0, 255, 0, 50), Color(0, 255, 0, 50)) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(Vec3d.ofBottomCenter(pos).playerBox(), Color(0, 255, 0, 50), Color(0, 255, 0, 50)) + } } } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt index e4a40a9f7..11bdef8d2 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt @@ -18,7 +18,8 @@ package com.lambda.interaction.construction.simulation.context import com.lambda.context.Automated -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.esp.ShapeScope +import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.material.StackSelection import com.lambda.interaction.managers.LogContext import com.lambda.interaction.managers.LogContext.Companion.LogContextBuilder @@ -63,14 +64,10 @@ data class BreakContext( override val sorter get() = breakConfig.sorter - override fun ShapeBuilder.buildRenderer() { - val box = with(hitResult.pos) { - Box( - x - 0.05, y - 0.05, z - 0.05, - x + 0.05, y + 0.05, z + 0.05, - ).offset(hitResult.side.doubleVector.multiply(0.05)) + override fun render(esp: TransientRegionESP) { + esp.shapes(blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble()) { + box(blockPos, baseColor, sideColor) } - box(box, baseColor, sideColor) } override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt index 0ccc754e6..6bc7cdbd3 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt @@ -18,7 +18,8 @@ package com.lambda.interaction.construction.simulation.context import com.lambda.context.Automated -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.esp.ShapeScope +import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.managers.LogContext import com.lambda.interaction.managers.LogContext.Companion.LogContextBuilder import com.lambda.interaction.managers.LogContext.Companion.getLogContextBuilder @@ -49,14 +50,16 @@ data class InteractContext( override val sorter get() = interactConfig.sorter - override fun ShapeBuilder.buildRenderer() { - val box = with(hitResult.pos) { - Box( - x - 0.05, y - 0.05, z - 0.05, - x + 0.05, y + 0.05, z + 0.05, - ).offset(hitResult.side.doubleVector.multiply(0.05)) + override fun render(esp: TransientRegionESP) { + esp.shapes(hitResult.pos.x, hitResult.pos.y, hitResult.pos.z) { + val box = with(hitResult.pos) { + Box( + x - 0.05, y - 0.05, z - 0.05, + x + 0.05, y + 0.05, z + 0.05, + ).offset(hitResult.side.doubleVector.multiply(0.05)) + } + box(box, baseColor, sideColor) } - box(box, baseColor, sideColor) } fun requestDependencies(request: InteractRequest): Boolean { diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Drawable.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Drawable.kt index cd51bd728..ac339712a 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Drawable.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Drawable.kt @@ -17,11 +17,11 @@ package com.lambda.interaction.construction.simulation.result -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.mc.TransientRegionESP /** * Represents a [BuildResult] that can be rendered in-game. */ interface Drawable { - fun ShapeBuilder.buildRenderer() + fun render(esp: TransientRegionESP) } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt index 100724f93..4356dc53d 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt @@ -22,7 +22,8 @@ import baritone.api.pathing.goals.GoalInverted import com.lambda.context.Automated import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.DirectionMask.mask -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.esp.ShapeScope +import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.construction.simulation.context.BreakContext import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.ComparableResult @@ -55,8 +56,8 @@ sealed class BreakResult : BuildResult() { ) : Contextual, Drawable, BreakResult() { override val rank = Rank.BreakSuccess - override fun ShapeBuilder.buildRenderer() { - with(context) { buildRenderer() } + override fun render(esp: TransientRegionESP) { + context.render(esp) } } @@ -72,8 +73,10 @@ sealed class BreakResult : BuildResult() { override val rank = Rank.BreakNotExposed private val color = Color(46, 0, 0, 30) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color, side.mask) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color, side.mask) + } } override fun compareResult(other: ComparableResult) = @@ -119,8 +122,10 @@ sealed class BreakResult : BuildResult() { override val rank = Rank.BreakSubmerge private val color = Color(114, 27, 255, 100) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color) + } } } @@ -135,13 +140,15 @@ sealed class BreakResult : BuildResult() { override val rank = Rank.BreakIsBlockedByFluid private val color = Color(50, 12, 112, 100) - override fun ShapeBuilder.buildRenderer() { - val center = pos.toCenterPos() - val box = Box( - center.x - 0.1, center.y - 0.1, center.z - 0.1, - center.x + 0.1, center.y + 0.1, center.z + 0.1 - ) - box(box, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + val center = pos.toCenterPos() + val box = Box( + center.x - 0.1, center.y - 0.1, center.z - 0.1, + center.x + 0.1, center.y + 0.1, center.z + 0.1 + ) + box(box, color, color) + } } } @@ -157,8 +164,10 @@ sealed class BreakResult : BuildResult() { override val goal = GoalInverted(GoalBlock(pos)) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color) + } } } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt index 6009ebaf4..dc8716f3f 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt @@ -20,7 +20,8 @@ package com.lambda.interaction.construction.simulation.result.results import baritone.api.pathing.goals.GoalNear import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.esp.ShapeScope +import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.ComparableResult import com.lambda.interaction.construction.simulation.result.Drawable @@ -53,14 +54,16 @@ sealed class GenericResult : BuildResult() { override val rank = Rank.NotVisible private val color = Color(46, 0, 0, 80) - override fun ShapeBuilder.buildRenderer() { - val box = with(pos) { - Box( - x - 0.05, y - 0.05, z - 0.05, - x + 0.05, y + 0.05, z + 0.05, - ).offset(pos) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + val box = with(pos) { + Box( + x - 0.05, y - 0.05, z - 0.05, + x + 0.05, y + 0.05, z + 0.05, + ).offset(pos) + } + box(box, color, color) } - box(box, color, color) } override fun compareResult(other: ComparableResult): Int { @@ -98,13 +101,15 @@ sealed class GenericResult : BuildResult() { context(automated: Automated, safeContext: SafeContext) override fun resolve() = neededSelection.transfer(MainHandContainer) - override fun ShapeBuilder.buildRenderer() { - val center = pos.toCenterPos() - val box = Box( - center.x - 0.1, center.y - 0.1, center.z - 0.1, - center.x + 0.1, center.y + 0.1, center.z + 0.1 - ) - box(box, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + val center = pos.toCenterPos() + val box = Box( + center.x - 0.1, center.y - 0.1, center.z - 0.1, + center.x + 0.1, center.y + 0.1, center.z + 0.1 + ) + box(box, color, color) + } } } @@ -129,13 +134,15 @@ sealed class GenericResult : BuildResult() { override val goal = GoalNear(pos, 3) - override fun ShapeBuilder.buildRenderer() { - val center = pos.toCenterPos() - val box = Box( - center.x - 0.1, center.y - 0.1, center.z - 0.1, - center.x + 0.1, center.y + 0.1, center.z + 0.1 - ) - box(box, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + val center = pos.toCenterPos() + val box = Box( + center.x - 0.1, center.y - 0.1, center.z - 0.1, + center.x + 0.1, center.y + 0.1, center.z + 0.1 + ) + box(box, color, color) + } } override fun compareResult(other: ComparableResult): Int { diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt index 653fa75ff..5aaf8aeb7 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt @@ -19,7 +19,8 @@ package com.lambda.interaction.construction.simulation.result.results import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.esp.ShapeScope +import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.construction.simulation.context.InteractContext import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.Contextual @@ -57,8 +58,8 @@ sealed class InteractResult : BuildResult() { ) : Contextual, Drawable, InteractResult() { override val rank = Rank.PlaceSuccess - override fun ShapeBuilder.buildRenderer() { - with(context) { buildRenderer() } + override fun render(esp: TransientRegionESP) { + context.render(esp) } } @@ -82,14 +83,16 @@ sealed class InteractResult : BuildResult() { override val rank = Rank.PlaceNoIntegrity private val color = Color(252, 3, 3, 100) - override fun ShapeBuilder.buildRenderer() { - val box = with(simulated.hitPos) { - Box( - x - 0.05, y - 0.05, z - 0.05, - x + 0.05, y + 0.05, z + 0.05, - ).offset(simulated.side.doubleVector.multiply(0.05)) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + val box = with(simulated.hitPos) { + Box( + x - 0.05, y - 0.05, z - 0.05, + x + 0.05, y + 0.05, z + 0.05, + ).offset(simulated.side.doubleVector.multiply(0.05)) + } + box(box, color, color) } - box(box, color, color) } } @@ -119,14 +122,16 @@ sealed class InteractResult : BuildResult() { override val rank = Rank.PlaceBlockedByEntity private val color = Color(252, 3, 3, 100) - override fun ShapeBuilder.buildRenderer() { - val box = with(hitPos) { - Box( - x - 0.05, y - 0.05, z - 0.05, - x + 0.05, y + 0.05, z + 0.05, - ).offset(side.doubleVector.multiply(0.05)) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + val box = with(hitPos) { + Box( + x - 0.05, y - 0.05, z - 0.05, + x + 0.05, y + 0.05, z + 0.05, + ).offset(side.doubleVector.multiply(0.05)) + } + box(box, color, color) } - box(box, color, color) } } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt index c92bda7b1..baa2a2513 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt @@ -18,7 +18,8 @@ package com.lambda.interaction.construction.simulation.result.results import baritone.api.pathing.goals.GoalBlock -import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.graphics.esp.ShapeScope +import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.ComparableResult import com.lambda.interaction.construction.simulation.result.Drawable @@ -55,8 +56,10 @@ sealed class PreSimResult : BuildResult() { override val goal = GoalBlock(pos) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color) + } } override fun compareResult(other: ComparableResult) = @@ -77,8 +80,10 @@ sealed class PreSimResult : BuildResult() { override val rank = Rank.BreakRestricted private val color = Color(255, 0, 0, 100) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color) + } } } @@ -95,8 +100,10 @@ sealed class PreSimResult : BuildResult() { override val rank get() = Rank.BreakNoPermission private val color = Color(255, 0, 0, 100) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color) + } } } @@ -111,8 +118,10 @@ sealed class PreSimResult : BuildResult() { override val rank = Rank.OutOfWorld private val color = Color(3, 148, 252, 100) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color) + } } } @@ -129,8 +138,10 @@ sealed class PreSimResult : BuildResult() { override val rank = Rank.Unbreakable private val color = Color(11, 11, 11, 100) - override fun ShapeBuilder.buildRenderer() { - box(pos, color, color) + override fun render(esp: TransientRegionESP) { + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + box(pos, color, color) + } } } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt index dedd3433e..1c7982091 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/breaking/BreakManager.kt @@ -27,6 +27,8 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.events.WorldEvent import com.lambda.event.events.onDynamicRender +import com.lambda.graphics.esp.ShapeScope + import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.graphics.renderer.esp.DynamicAABB @@ -231,7 +233,7 @@ object BreakManager : Manager( ?.internalOnItemDrop(it.entity) } - onDynamicRender { render -> + onDynamicRender { esp -> val activeStack = breakInfos .filterNotNull() .firstOrNull()?.swapStack ?: return@onDynamicRender @@ -276,15 +278,18 @@ object BreakManager : Manager( ) else config.staticOutlineColor - info.context.cachedState.getOutlineShape(world, info.context.blockPos).boundingBoxes.map { - it.offset(info.context.blockPos) - }.forEach boxes@ { box -> - val animationMode = info.breakConfig.animation - val currentProgress = interpolateBox(box, currentProgress, animationMode) - val nextProgress = interpolateBox(box, nextTicksProgress, animationMode) - val dynamicAABB = DynamicAABB().update(currentProgress).update(nextProgress) - if (config.fill) render.filled(dynamicAABB, fillColor) - if (config.outline) render.outline(dynamicAABB, outlineColor) + val pos = info.context.blockPos + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + info.context.cachedState.getOutlineShape(world, pos).boundingBoxes.map { + it.offset(pos) + }.forEach boxes@{ box -> + val animationMode = info.breakConfig.animation + val currentProgressBox = interpolateBox(box, currentProgress, animationMode) + val nextProgressBox = interpolateBox(box, nextTicksProgress, animationMode) + val dynamicAABB = DynamicAABB().update(currentProgressBox).update(nextProgressBox) + if (config.fill) filled(dynamicAABB, fillColor) + if (config.outline) outline(dynamicAABB, outlineColor) + } } } } diff --git a/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt b/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt index 1a649301c..499db8337 100644 --- a/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt +++ b/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt @@ -47,12 +47,14 @@ object BlockTest : Module( private val outlineColor = Color(100, 150, 255, 51) init { - onStaticRender { + onStaticRender { esp -> blockSearch(range, step = step) { _, state -> state.isOf(Blocks.DIAMOND_BLOCK) }.forEach { (pos, state) -> - state.getOutlineShape(world, pos).boundingBoxes.forEach { box -> - it.box(box.offset(pos), filledColor, outlineColor) + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + state.getOutlineShape(world, pos).boundingBoxes.forEach { box -> + box(box.offset(pos), filledColor, outlineColor) + } } } } diff --git a/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index c6a68efef..7520f308a 100644 --- a/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -45,15 +45,19 @@ object RenderTest : Module( private val filledColor = outlineColor.setAlpha(0.2) init { - onDynamicRender { + onDynamicRender { esp -> entitySearch(8.0) .forEach { entity -> - it.box(entity.dynamicBox, filledColor, outlineColor, DirectionMask.ALL, DirectionMask.OutlineMode.And) + esp.shapes(entity.x, entity.y, entity.z) { + box(entity.dynamicBox, filledColor, outlineColor, DirectionMask.ALL, DirectionMask.OutlineMode.And) + } } } - onStaticRender { - it.box(Box.of(player.pos, 0.3, 0.3, 0.3), filledColor, outlineColor) + onStaticRender { esp -> + esp.shapes(player.x, player.y, player.z) { + box(Box.of(player.pos, 0.3, 0.3, 0.3), filledColor, outlineColor) + } } } } diff --git a/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt b/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt index cae9fd743..a6b255d66 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/BackTrack.kt @@ -23,6 +23,7 @@ import com.lambda.event.events.PacketEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.onDynamicRender import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.esp.ShapeScope import com.lambda.graphics.renderer.esp.DynamicAABB import com.lambda.gui.components.ClickGuiLayout import com.lambda.module.Module @@ -110,7 +111,7 @@ object BackTrack : Module( poolPackets() } - onDynamicRender { + onDynamicRender { esp -> val target = target ?: return@onDynamicRender val c1 = ClickGuiLayout.primaryColor @@ -118,7 +119,9 @@ object BackTrack : Module( val p = target.hurtTime / 10.0 val c = lerp(p, c1, c2) - it.box(box, c.multAlpha(0.3), c.multAlpha(0.8)) + esp.shapes(target.pos.x, target.pos.y, target.pos.z) { + box(box, c.multAlpha(0.3), c.multAlpha(0.8)) + } } listen { event -> diff --git a/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt b/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt index 5c4f4848b..f94ddb8fd 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/Blink.kt @@ -22,6 +22,7 @@ import com.lambda.event.events.PacketEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.onDynamicRender import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.esp.ShapeScope import com.lambda.graphics.renderer.esp.DynamicAABB import com.lambda.gui.components.ClickGuiLayout import com.lambda.module.Module @@ -67,9 +68,12 @@ object Blink : Module( poolPackets() } - onDynamicRender { + onDynamicRender { esp -> val color = ClickGuiLayout.primaryColor - it.box(box.update(lastBox), color.setAlpha(0.3), color) + val pos = player.pos + esp.shapes(pos.x, pos.y, pos.z) { + box(box.update(lastBox), color.setAlpha(0.3), color) + } } listen { event -> diff --git a/src/main/kotlin/com/lambda/module/modules/player/AirPlace.kt b/src/main/kotlin/com/lambda/module/modules/player/AirPlace.kt index 3aef26543..2a0858f3e 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AirPlace.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AirPlace.kt @@ -106,12 +106,14 @@ object AirPlace : Module( listen { if (airPlace()) it.cancel() } listen { if (airPlace()) it.cancel() } - onStaticRender { event -> + onStaticRender { esp -> placementPos?.let { pos -> val boxes = placementState?.getOutlineShape(world, pos)?.boundingBoxes ?: listOf(Box(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)) - boxes.forEach { box -> - event.outline(box.offset(pos), outlineColor) + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + boxes.forEach { box -> + outline(box.offset(pos), outlineColor) + } } } } diff --git a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt index 168b27939..bad19e5f7 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/PacketMine.kt @@ -171,9 +171,13 @@ object PacketMine : Module( } } - onStaticRender { event -> + onStaticRender { esp -> if (renderRebreak) { - rebreakPos?.let { event.outline(it, rebreakColor) } + rebreakPos?.let { pos -> + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + outline(pos, rebreakColor) + } + } } if (!renderQueue) return@onStaticRender queueSorted.forEachIndexed { index, positions -> @@ -185,8 +189,10 @@ object PacketMine : Module( RenderMode.Box -> listOf(Box(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)) }.map { lerp(renderSize.toDouble(), Box(it.center, it.center), it).offset(pos) } - boxes.forEach { box -> - event.box(box, color, color.setAlpha(1.0)) + esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { + boxes.forEach { box -> + box(box, color, color.setAlpha(1.0)) + } } } } diff --git a/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt b/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt index 3d45ed445..c97f8242a 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/WorldEater.kt @@ -65,7 +65,11 @@ object WorldEater : Module( BaritoneManager.cancel() } - onStaticRender { it.outline(Box.enclosing(pos1, pos2), Color.BLUE) } + onStaticRender { esp -> + esp.shapes(pos1.x.toDouble(), pos1.y.toDouble(), pos1.z.toDouble()) { + outline(Box.enclosing(pos1, pos2), Color.BLUE) + } + } } private fun buildLayer() { diff --git a/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt index 5ac2dd143..c7110da09 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -21,8 +21,11 @@ import com.lambda.context.SafeContext import com.lambda.graphics.esp.ShapeScope import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh +import com.lambda.event.events.onStaticRender import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.world.blockEntitySearch +import com.lambda.util.world.entitySearch import com.lambda.threading.runSafe import com.lambda.util.NamedEnum import com.lambda.util.extension.blockColor @@ -107,23 +110,29 @@ object StorageESP : Module( ) init { - // onStaticRender { render -> - // blockEntitySearch(distance) - // .filter { it::class in entities } - // .forEach { render.shapes(it.pos) { build(it, excludedSides(it)) } } - // - // val mineCarts = - // entitySearch(distance).filter { - // it::class in entities - // } - // val itemFrames = - // entitySearch(distance).filter { - // it::class in entities - // } - // (mineCarts + itemFrames).forEach { - // render.shapes(it.blockPos) { build(it, DirectionMask.ALL) } - // } - // } + onStaticRender { esp -> + blockEntitySearch(distance) + .filter { it::class in entities } + .forEach { be -> + esp.shapes(be.pos.x.toDouble(), be.pos.y.toDouble(), be.pos.z.toDouble()) { + build(be, excludedSides(be)) + } + } + + val mineCarts = + entitySearch(distance).filter { + it::class in entities + } + val itemFrames = + entitySearch(distance).filter { + it::class in entities + } + (mineCarts + itemFrames).forEach { entity -> + esp.shapes(entity.getX(), entity.getY(), entity.getZ()) { + build(entity, DirectionMask.ALL) + } + } + } } private fun SafeContext.excludedSides(blockEntity: BlockEntity): Int { From f529eaa6d0876c023badfcc92458d57de8ec78a9 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Sat, 20 Dec 2025 00:58:27 +0000 Subject: [PATCH 16/20] 1.21.11 changes --- .../java/com/lambda/mixin/render/HeldItemRendererMixin.java | 2 +- .../lambda/interaction/managers/rotating/RotationRequest.kt | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java b/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java index ef2d89872..0be8b99b8 100644 --- a/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/HeldItemRendererMixin.java @@ -64,7 +64,7 @@ private float modifyEquipProgressMainHand(float value) { mainHand = currentStack; } - float progress = config.getOldAnimations() ? 1 : (float) Math.pow(client.player.getAttackCooldownProgress(1), 3); + float progress = config.getOldAnimations() ? 1 : (float) Math.pow(client.player.getHandEquippingProgress(1), 3); return (ItemStack.areEqual(mainHand, currentStack) ? progress : 0) - equipProgressMainHand; } diff --git a/src/main/kotlin/com/lambda/interaction/managers/rotating/RotationRequest.kt b/src/main/kotlin/com/lambda/interaction/managers/rotating/RotationRequest.kt index febb15543..7cad72f60 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/rotating/RotationRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/rotating/RotationRequest.kt @@ -49,9 +49,7 @@ data class RotationRequest( var age = 0 override val nowOrNothing = false - override val done: Boolean get() { - return RotationManager.activeRotation.dist(rotation.value ?: return false) <= 0.001 - } + override val done: Boolean get() = RotationManager.activeRotation.dist(rotation.value ?: return false) <= 0.001 override fun submit(queueIfMismatchedStage: Boolean): RotationRequest = RotationManager.request(this, queueIfMismatchedStage) From 2f58ca525dfa8c4d26203788ed8ddac8ebb1dcb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emy=20=F0=9F=92=9C?= Date: Sat, 20 Dec 2025 09:50:00 -0500 Subject: [PATCH 17/20] removed setting type bound --- src/main/kotlin/com/lambda/config/Setting.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/lambda/config/Setting.kt b/src/main/kotlin/com/lambda/config/Setting.kt index 9f16d53c4..75f9ed688 100644 --- a/src/main/kotlin/com/lambda/config/Setting.kt +++ b/src/main/kotlin/com/lambda/config/Setting.kt @@ -94,7 +94,7 @@ import kotlin.reflect.KProperty * @property type The type reflection of the setting. * @property visibility A function that determines whether the setting is visible. */ -abstract class SettingCore( +abstract class SettingCore( var defaultValue: T, val type: Type ) { @@ -150,7 +150,7 @@ abstract class SettingCore( } } -class Setting, R : Any>( +class Setting, R>( override val name: String, override val description: String, var core: T, From aec823dae6118fc50280608d24a36cf76821a00c Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Sat, 20 Dec 2025 15:24:08 +0000 Subject: [PATCH 18/20] queue rotation request in anti aim --- src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt b/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt index 17b144120..4f10ffdcc 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AntiAim.kt @@ -162,7 +162,7 @@ object AntiAim : Module( listen(priority = Int.MIN_VALUE) { if (currentYaw == wrap(player.yaw) && currentPitch == player.pitch) return@listen - submit(RotationRequest(Rotation(currentYaw, currentPitch), this@AntiAim), false) + submit(RotationRequest(Rotation(currentYaw, currentPitch), this@AntiAim)) } } From cc7616e7c20af981e52fee1b70f03f3bd49439e8 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Dec 2025 18:40:05 +0100 Subject: [PATCH 19/20] Custom line shader --- .../com/lambda/graphics/esp/ShapeScope.kt | 89 +++++++----- .../graphics/mc/LambdaRenderPipelines.kt | 12 +- .../com/lambda/graphics/mc/RegionRenderer.kt | 4 +- .../lambda/graphics/mc/RegionShapeBuilder.kt | 134 ++++++++++-------- .../graphics/mc/RegionVertexCollector.kt | 2 +- .../lambda/module/modules/render/BlockESP.kt | 3 +- .../lambda/module/modules/render/EntityESP.kt | 4 +- .../module/modules/render/StorageESP.kt | 5 +- .../lambda/shaders/core/advanced_lines.fsh | 50 +++++++ .../lambda/shaders/core/advanced_lines.vsh | 74 ++++++++++ 10 files changed, 266 insertions(+), 111 deletions(-) create mode 100644 src/main/resources/assets/lambda/shaders/core/advanced_lines.fsh create mode 100644 src/main/resources/assets/lambda/shaders/core/advanced_lines.vsh diff --git a/src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt b/src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt index 93705b4ec..14ab277f5 100644 --- a/src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt +++ b/src/main/kotlin/com/lambda/graphics/esp/ShapeScope.kt @@ -47,7 +47,8 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { scope.filledColor, scope.outlineColor, scope.sides, - scope.outlineMode + scope.outlineMode, + scope.thickness ) ) } @@ -98,10 +99,10 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { } /** Draw a simple outlined box. */ - fun outline(box: Box, color: Color, sides: Int = DirectionMask.ALL) { - builder.outline(box, color, sides) + fun outline(box: Box, color: Color, sides: Int = DirectionMask.ALL, thickness: Float = builder.lineWidth) { + builder.outline(box, color, sides, thickness = thickness) if (collectShapes) { - shapes?.add(EspShape.BoxShape(box.hashCode(), box, null, color, sides)) + shapes?.add(EspShape.BoxShape(box.hashCode(), box, null, color, sides, thickness = thickness)) } } @@ -114,11 +115,11 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { } } - fun outline(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL) { - builder.outline(box, color, sides) + fun outline(box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL, thickness: Float = builder.lineWidth) { + builder.outline(box, color, sides, thickness = thickness) if (collectShapes) { box.pair?.second?.let { - shapes?.add(EspShape.BoxShape(it.hashCode(), it, null, color, sides)) + shapes?.add(EspShape.BoxShape(it.hashCode(), it, null, color, sides, thickness = thickness)) } } } @@ -130,10 +131,10 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { } } - fun outline(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL) { - builder.outline(pos, color, sides) + fun outline(pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL, thickness: Float = builder.lineWidth) { + builder.outline(pos, color, sides, thickness = thickness) if (collectShapes) { - shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), null, color, sides)) + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), null, color, sides, thickness = thickness)) } } @@ -144,10 +145,10 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { } } - fun outline(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL) { - builder.outline(pos, state, color, sides) + fun outline(pos: BlockPos, state: BlockState, color: Color, sides: Int = DirectionMask.ALL, thickness: Float = builder.lineWidth) { + builder.outline(pos, state, color, sides, thickness = thickness) if (collectShapes) { - shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), null, color, sides)) + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), null, color, sides, thickness = thickness)) } } @@ -160,11 +161,11 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { } } - fun outline(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL) { - builder.outline(shape, color, sides) + fun outline(shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL, thickness: Float = builder.lineWidth) { + builder.outline(shape, color, sides, thickness = thickness) if (collectShapes) { shape.boundingBoxes.forEach { - shapes?.add(EspShape.BoxShape(it.hashCode(), it, null, color, sides)) + shapes?.add(EspShape.BoxShape(it.hashCode(), it, null, color, sides, thickness = thickness)) } } } @@ -175,11 +176,12 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filled: Color, outline: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = builder.lineWidth ) { - builder.box(pos, state, filled, outline, sides, mode) + builder.box(pos, state, filled, outline, sides, mode, thickness = thickness) if (collectShapes) { - shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), filled, outline, sides, mode)) + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), filled, outline, sides, mode, thickness = thickness)) } } @@ -188,11 +190,12 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filled: Color, outline: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = builder.lineWidth ) { - builder.box(pos, filled, outline, sides, mode) + builder.box(pos, filled, outline, sides, mode, thickness = thickness) if (collectShapes) { - shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), filled, outline, sides, mode)) + shapes?.add(EspShape.BoxShape(pos.hashCode(), Box(pos), filled, outline, sides, mode, thickness = thickness)) } } @@ -201,11 +204,12 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = builder.lineWidth ) { - builder.box(box, filledColor, outlineColor, sides, mode) + builder.box(box, filledColor, outlineColor, sides, mode, thickness = thickness) if (collectShapes) { - shapes?.add(EspShape.BoxShape(box.hashCode(), box, filledColor, outlineColor, sides, mode)) + shapes?.add(EspShape.BoxShape(box.hashCode(), box, filledColor, outlineColor, sides, mode, thickness = thickness)) } } @@ -214,13 +218,14 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = builder.lineWidth ) { - builder.box(box, filledColor, outlineColor, sides, mode) + builder.box(box, filledColor, outlineColor, sides, mode, thickness = thickness) if (collectShapes) { box.pair?.second?.let { shapes?.add( - EspShape.BoxShape(it.hashCode(), it, filledColor, outlineColor, sides, mode) + EspShape.BoxShape(it.hashCode(), it, filledColor, outlineColor, sides, mode, thickness = thickness) ) } } @@ -231,9 +236,10 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filled: Color, outline: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = builder.lineWidth ) { - builder.box(entity, filled, outline, sides, mode) + builder.box(entity, filled, outline, sides, mode, thickness = thickness) if (collectShapes) { shapes?.add( EspShape.BoxShape( @@ -242,7 +248,8 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filled, outline, sides, - mode + mode, + thickness = thickness ) ) } @@ -253,9 +260,10 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filled: Color, outline: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = builder.lineWidth ) { - builder.box(entity, filled, outline, sides, mode) + builder.box(entity, filled, outline, sides, mode, thickness = thickness) if (collectShapes) { shapes?.add( EspShape.BoxShape( @@ -264,7 +272,8 @@ class ShapeScope(val region: RenderRegion, val collectShapes: Boolean = false) { filled, outline, sides, - mode + mode, + thickness = thickness ) ) } @@ -277,6 +286,7 @@ class BoxScope(val box: Box, val parent: ShapeScope) { internal var outlineColor: Color? = null internal var sides: Int = DirectionMask.ALL internal var outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + internal var thickness: Float = parent.builder.lineWidth fun filled(color: Color, sides: Int = DirectionMask.ALL) { this.filledColor = color @@ -287,12 +297,14 @@ class BoxScope(val box: Box, val parent: ShapeScope) { fun outline( color: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = parent.builder.lineWidth ) { this.outlineColor = color this.sides = sides this.outlineMode = mode - parent.builder.outline(box, color, sides, mode) + this.thickness = thickness + parent.builder.outline(box, color, sides, mode, thickness = thickness) } } @@ -342,7 +354,8 @@ sealed class EspShape(val id: Int) { val filledColor: Color?, val outlineColor: Color?, val sides: Int = DirectionMask.ALL, - val outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + val outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + val thickness: Float = 1.0f ) : EspShape(id) { override fun renderInterpolated( prev: EspShape, @@ -364,7 +377,7 @@ sealed class EspShape(val id: Int) { val shapeBuilder = RegionShapeBuilder(region) filledColor?.let { shapeBuilder.filled(interpBox, it, sides) } - outlineColor?.let { shapeBuilder.outline(interpBox, it, sides, outlineMode) } + outlineColor?.let { shapeBuilder.outline(interpBox, it, sides, outlineMode, thickness = thickness) } collector.faceVertices.addAll(shapeBuilder.collector.faceVertices) collector.edgeVertices.addAll(shapeBuilder.collector.edgeVertices) diff --git a/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt index eb070ef00..8f3541d37 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt @@ -92,15 +92,15 @@ object LambdaRenderPipelines : Loadable { RenderPipelines.register( RenderPipeline.builder(LAMBDA_ESP_SNIPPET, RenderPipelines.GLOBALS_SNIPPET) .withLocation(Identifier.of("lambda", "pipeline/esp_lines")) - .withVertexShader(Identifier.ofVanilla("core/rendertype_lines")) - .withFragmentShader(Identifier.ofVanilla("core/rendertype_lines")) + .withVertexShader(Identifier.of("lambda", "core/advanced_lines")) + .withFragmentShader(Identifier.of("lambda", "core/advanced_lines")) .withBlend(BlendFunction.TRANSLUCENT) .withDepthWrite(false) .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) .withCull(false) .withVertexFormat( VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, - VertexFormat.DrawMode.LINES + VertexFormat.DrawMode.QUADS ) .build() ) @@ -110,15 +110,15 @@ object LambdaRenderPipelines : Loadable { RenderPipelines.register( RenderPipeline.builder(LAMBDA_ESP_SNIPPET, RenderPipelines.GLOBALS_SNIPPET) .withLocation(Identifier.of("lambda", "pipeline/esp_lines_through")) - .withVertexShader(Identifier.ofVanilla("core/rendertype_lines")) - .withFragmentShader(Identifier.ofVanilla("core/rendertype_lines")) + .withVertexShader(Identifier.of("lambda", "core/advanced_lines")) + .withFragmentShader(Identifier.of("lambda", "core/advanced_lines")) .withBlend(BlendFunction.TRANSLUCENT) .withDepthWrite(false) .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) .withCull(false) .withVertexFormat( VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, - VertexFormat.DrawMode.LINES + VertexFormat.DrawMode.QUADS ) .build() ) diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt index 1daba012d..8d7f36f4a 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionRenderer.kt @@ -96,8 +96,8 @@ class RegionRenderer(val region: RenderRegion) { if (edgeIndexCount == 0) return renderPass.setVertexBuffer(0, vb) - // Use vanilla's sequential index buffer for lines - val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.LINES) + // Use vanilla's sequential index buffer for quads + val shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS) val indexBuffer = shapeIndexBuffer.getIndexBuffer(edgeIndexCount) renderPass.setIndexBuffer(indexBuffer, shapeIndexBuffer.indexType) diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt index f437b7012..8b33498be 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionShapeBuilder.kt @@ -49,7 +49,7 @@ import kotlin.math.sqrt class RegionShapeBuilder(val region: RenderRegion) { val collector = RegionVertexCollector() - private val lineWidth: Float + val lineWidth: Float get() = StyleEditor.outlineWidth.toFloat() fun box( @@ -181,7 +181,8 @@ class RegionShapeBuilder(val region: RenderRegion) { bottomColor: Color, topColor: Color = bottomColor, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) { val (x1, y1, z1) = toRelative(box.minX, box.minY, box.minZ) val (x2, y2, z2) = toRelative(box.maxX, box.maxY, box.maxZ) @@ -194,36 +195,38 @@ class RegionShapeBuilder(val region: RenderRegion) { val hasNorth = sides.hasDirection(DirectionMask.NORTH) // Top edges - if (mode.check(hasUp, hasNorth)) line(x1, y2, z1, x2, y2, z1, topColor) - if (mode.check(hasUp, hasSouth)) line(x1, y2, z2, x2, y2, z2, topColor) - if (mode.check(hasUp, hasWest)) line(x1, y2, z1, x1, y2, z2, topColor) - if (mode.check(hasUp, hasEast)) line(x2, y2, z2, x2, y2, z1, topColor) + if (mode.check(hasUp, hasNorth)) line(x1, y2, z1, x2, y2, z1, topColor, topColor, thickness) + if (mode.check(hasUp, hasSouth)) line(x1, y2, z2, x2, y2, z2, topColor, topColor, thickness) + if (mode.check(hasUp, hasWest)) line(x1, y2, z1, x1, y2, z2, topColor, topColor, thickness) + if (mode.check(hasUp, hasEast)) line(x2, y2, z2, x2, y2, z1, topColor, topColor, thickness) // Bottom edges - if (mode.check(hasDown, hasNorth)) line(x1, y1, z1, x2, y1, z1, bottomColor) - if (mode.check(hasDown, hasSouth)) line(x1, y1, z2, x2, y1, z2, bottomColor) - if (mode.check(hasDown, hasWest)) line(x1, y1, z1, x1, y1, z2, bottomColor) - if (mode.check(hasDown, hasEast)) line(x2, y1, z1, x2, y1, z2, bottomColor) + if (mode.check(hasDown, hasNorth)) line(x1, y1, z1, x2, y1, z1, bottomColor, bottomColor, thickness) + if (mode.check(hasDown, hasSouth)) line(x1, y1, z2, x2, y1, z2, bottomColor, bottomColor, thickness) + if (mode.check(hasDown, hasWest)) line(x1, y1, z1, x1, y1, z2, bottomColor, bottomColor, thickness) + if (mode.check(hasDown, hasEast)) line(x2, y1, z1, x2, y1, z2, bottomColor, bottomColor, thickness) // Vertical edges - if (mode.check(hasWest, hasNorth)) line(x1, y2, z1, x1, y1, z1, topColor, bottomColor) - if (mode.check(hasNorth, hasEast)) line(x2, y2, z1, x2, y1, z1, topColor, bottomColor) - if (mode.check(hasEast, hasSouth)) line(x2, y2, z2, x2, y1, z2, topColor, bottomColor) - if (mode.check(hasSouth, hasWest)) line(x1, y2, z2, x1, y1, z2, topColor, bottomColor) + if (mode.check(hasWest, hasNorth)) line(x1, y2, z1, x1, y1, z1, topColor, bottomColor, thickness) + if (mode.check(hasNorth, hasEast)) line(x2, y2, z1, x2, y1, z1, topColor, bottomColor, thickness) + if (mode.check(hasEast, hasSouth)) line(x2, y2, z2, x2, y1, z2, topColor, bottomColor, thickness) + if (mode.check(hasSouth, hasWest)) line(x1, y2, z2, x1, y1, z2, topColor, bottomColor, thickness) } fun outline( box: Box, color: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And - ) = outline(box, color, color, sides, mode) + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth + ) = outline(box, color, color, sides, mode, thickness) fun outline( box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) { val pair = box.pair ?: return val prev = pair.first @@ -237,7 +240,7 @@ class RegionShapeBuilder(val region: RenderRegion) { lerp(tickDelta, prev.maxY, curr.maxY), lerp(tickDelta, prev.maxZ, curr.maxZ) ) - outline(interpolated, color, sides, mode) + outline(interpolated, color, sides, mode, thickness) } fun outline( @@ -245,13 +248,14 @@ class RegionShapeBuilder(val region: RenderRegion) { state: BlockState, color: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) = runSafe { val shape = state.getOutlineShape(world, pos) if (shape.isEmpty) { - outline(Box(pos), color, sides, mode) + outline(Box(pos), color, sides, mode, thickness) } else { - outline(shape.offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()), color, sides, mode) + outline(shape.offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()), color, sides, mode, thickness) } } @@ -259,24 +263,27 @@ class RegionShapeBuilder(val region: RenderRegion) { pos: BlockPos, color: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And - ) = runSafe { outline(pos, blockState(pos), color, sides, mode) } + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth + ) = runSafe { outline(pos, blockState(pos), color, sides, mode, thickness) } fun outline( pos: BlockPos, entity: BlockEntity, color: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And - ) = runSafe { outline(pos, entity.cachedState, color, sides, mode) } + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth + ) = runSafe { outline(pos, entity.cachedState, color, sides, mode, thickness) } fun outline( shape: VoxelShape, color: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) { - shape.boundingBoxes.forEach { outline(it, color, sides, mode) } + shape.boundingBoxes.forEach { outline(it, color, sides, mode, thickness) } } /** Add both filled and outline for a box. */ @@ -286,10 +293,11 @@ class RegionShapeBuilder(val region: RenderRegion) { filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) = runSafe { filled(pos, state, filledColor, sides) - outline(pos, state, outlineColor, sides, mode) + outline(pos, state, outlineColor, sides, mode, thickness) } fun box( @@ -297,10 +305,11 @@ class RegionShapeBuilder(val region: RenderRegion) { filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) = runSafe { filled(pos, filledColor, sides) - outline(pos, outlineColor, sides, mode) + outline(pos, outlineColor, sides, mode, thickness) } fun box( @@ -308,10 +317,11 @@ class RegionShapeBuilder(val region: RenderRegion) { filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) { filled(box, filledColor, sides) - outline(box, outlineColor, sides, mode) + outline(box, outlineColor, sides, mode, thickness) } fun box( @@ -319,30 +329,35 @@ class RegionShapeBuilder(val region: RenderRegion) { filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) { filled(box, filledColor, sides) - outline(box, outlineColor, sides, mode) + outline(box, outlineColor, sides, mode, thickness) } fun box( entity: BlockEntity, - color: Color, + filled: Color, + outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) = runSafe { - filled(entity.pos, entity, color, sides) - outline(entity.pos, entity, color, sides, mode) + filled(entity.pos, entity, filled, sides) + outline(entity.pos, entity, outlineColor, sides, mode, thickness) } fun box( entity: Entity, - color: Color, + filled: Color, + outlineColor: Color, sides: Int = DirectionMask.ALL, - mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And + mode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.And, + thickness: Float = lineWidth ) = runSafe { - filled(entity.boundingBox, color, sides) - outline(entity.boundingBox, color, sides, mode) + filled(entity.boundingBox, filled, sides) + outline(entity.boundingBox, outlineColor, sides, mode, thickness) } private fun faceVertex(x: Float, y: Float, z: Float, color: Color) { @@ -356,9 +371,10 @@ class RegionShapeBuilder(val region: RenderRegion) { x2: Float, y2: Float, z2: Float, - color: Color + color: Color, + width: Float = lineWidth ) { - line(x1, y1, z1, x2, y2, z2, color, color) + line(x1, y1, z1, x2, y2, z2, color, color, width) } private fun line( @@ -369,19 +385,20 @@ class RegionShapeBuilder(val region: RenderRegion) { y2: Float, z2: Float, color1: Color, - color2: Color + color2: Color, + width: Float = lineWidth ) { - // Calculate normal (direction of line) + // Calculate segment vector (dx, dy, dz) val dx = x2 - x1 val dy = y2 - y1 val dz = z2 - z1 - val len = sqrt(dx * dx + dy * dy + dz * dz) - val nx = if (len > 0) dx / len else 0f - val ny = if (len > 0) dy / len else 1f - val nz = if (len > 0) dz / len else 0f - collector.addEdgeVertex(x1, y1, z1, color1, nx, ny, nz, lineWidth) - collector.addEdgeVertex(x2, y2, z2, color2, nx, ny, nz, lineWidth) + // Quad-based lines need 4 vertices per segment + // We pass the full vector as 'Normal' so the shader knows where the other end is + collector.addEdgeVertex(x1, y1, z1, color1, dx, dy, dz, width) + collector.addEdgeVertex(x1, y1, z1, color1, dx, dy, dz, width) + collector.addEdgeVertex(x2, y2, z2, color2, dx, dy, dz, width) + collector.addEdgeVertex(x2, y2, z2, color2, dx, dy, dz, width) } /** @@ -733,12 +750,9 @@ class RegionShapeBuilder(val region: RenderRegion) { val dx = x2 - x1 val dy = y2 - y1 val dz = z2 - z1 - val len = sqrt(dx * dx + dy * dy + dz * dz) - val nx = if (len > 0) dx / len else 0f - val ny = if (len > 0) dy / len else 1f - val nz = if (len > 0) dz / len else 0f - - collector.addEdgeVertex(x1, y1, z1, color, nx, ny, nz, width) - collector.addEdgeVertex(x2, y2, z2, color, nx, ny, nz, width) + collector.addEdgeVertex(x1, y1, z1, color, dx, dy, dz, width) + collector.addEdgeVertex(x1, y1, z1, color, dx, dy, dz, width) + collector.addEdgeVertex(x2, y2, z2, color, dx, dy, dz, width) + collector.addEdgeVertex(x2, y2, z2, color, dx, dy, dz, width) } } diff --git a/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt b/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt index e773a61fb..c82347817 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/RegionVertexCollector.kt @@ -137,7 +137,7 @@ class RegionVertexCollector { val builder = BufferBuilder( allocator, - VertexFormat.DrawMode.LINES, + VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH ) diff --git a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index fc9789b4c..1170f37d2 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -52,6 +52,7 @@ object BlockESP : Module( private val useBlockColor by setting("Use Block Color", false, "Use the color of the block instead") { searchBlocks }.onValueChange(::rebuildMesh) private val faceColor by setting("Face Color", Color(100, 150, 255, 51), "Color of the surfaces") { searchBlocks && drawFaces && !useBlockColor }.onValueChange(::rebuildMesh) private val outlineColor by setting("Outline Color", Color(100, 150, 255, 128), "Color of the outlines") { searchBlocks && drawOutlines && !useBlockColor }.onValueChange(::rebuildMesh) + private val outlineWidth by setting("Outline Width", 1.0f, 0.5f..5.0f, 0.5f) { searchBlocks && drawOutlines }.onValueChange(::rebuildMesh) private val outlineMode by setting("Outline Mode", DirectionMask.OutlineMode.And, "Outline mode") { searchBlocks }.onValueChange(::rebuildMesh) @@ -84,7 +85,7 @@ object BlockESP : Module( if (drawFaces) filled(if (useBlockColor) extractedColor else faceColor, sides) if (drawOutlines) - outline(if (useBlockColor) extractedColor else BlockESP.outlineColor, sides, BlockESP.outlineMode) + outline(if (useBlockColor) extractedColor else BlockESP.outlineColor, sides, BlockESP.outlineMode, thickness = outlineWidth) } } } diff --git a/src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt b/src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt index 7df039dd7..506a98a2f 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/EntityESP.kt @@ -85,6 +85,7 @@ object EntityESP : Module( private val drawOutline by setting("Outline", true, "Draw box outlines") { drawBoxes }.group(Group.Render) private val filledAlpha by setting("Filled Alpha", 0.2, 0.0..1.0, 0.05) { drawBoxes && drawFilled }.group(Group.Render) private val outlineAlpha by setting("Outline Alpha", 0.8, 0.0..1.0, 0.05) { drawBoxes && drawOutline }.group(Group.Render) + private val outlineWidth by setting("Outline Width", 1.0f, 0.5f..5.0f, 0.5f) { drawBoxes && drawOutline }.group(Group.Render) private val tracers by setting("Tracers", true, "Draw lines to entities").group(Group.Tracers) private val tracerOrigin by setting("Tracer Origin", TracerOrigin.Eyes, "Where tracers start from") { tracers }.group(Group.Tracers) @@ -123,7 +124,8 @@ object EntityESP : Module( filled(color.setAlpha(filledAlpha)) if (drawOutline) outline( - color.setAlpha(outlineAlpha) + color.setAlpha(outlineAlpha), + thickness = outlineWidth ) } } diff --git a/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt index c7110da09..e20dbe40d 100644 --- a/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt +++ b/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -61,6 +61,7 @@ object StorageESP : Module( private val useBlockColor by setting("Use Block Color", true, "Use the color of the block instead").group(Group.Color) private val facesAlpha by setting("Faces Alpha", 0.3, 0.1..1.0, 0.05).group(Group.Color) private val edgesAlpha by setting("Edges Alpha", 0.3, 0.1..1.0, 0.05).group(Group.Color) + private val outlineWidth by setting("Outline Width", 1.0f, 0.5f..5.0f, 0.5f) { drawEdges }.group(Group.Render) // TODO: // val blockColors by setting("Block Colors", mapOf()) { page == Page.Color @@ -155,12 +156,12 @@ object StorageESP : Module( val color = if (useBlockColor) blockColor(block.cachedState, block.pos) else block.color ?: return@runSafe - box(block, color.setAlpha(facesAlpha), color.setAlpha(edgesAlpha), sides, mode) + box(block, color.setAlpha(facesAlpha), color.setAlpha(edgesAlpha), sides, mode, thickness = outlineWidth) } private fun ShapeScope.build(entity: Entity, sides: Int) = runSafe { val color = entity.color ?: return@runSafe - box(entity, color.setAlpha(facesAlpha), color.setAlpha(edgesAlpha), sides, mode) + box(entity, color.setAlpha(facesAlpha), color.setAlpha(edgesAlpha), sides, mode, thickness = outlineWidth) } private val BlockEntity?.color diff --git a/src/main/resources/assets/lambda/shaders/core/advanced_lines.fsh b/src/main/resources/assets/lambda/shaders/core/advanced_lines.fsh new file mode 100644 index 000000000..7727eca9d --- /dev/null +++ b/src/main/resources/assets/lambda/shaders/core/advanced_lines.fsh @@ -0,0 +1,50 @@ +#version 330 + +#moj_import +#moj_import + +in vec4 vertexColor; +noperspective in float v_LineDist; +noperspective in float v_LineWidth; +noperspective in vec2 v_DistPixels; +noperspective in float v_LineLength; +in float sphericalVertexDistance; +in float cylindricalVertexDistance; + +out vec4 fragColor; + +void main() { + // Closest point on the center line segment [0, L] + float closestX = clamp(v_DistPixels.x, 0.0, v_LineLength); + vec2 closestPoint = vec2(closestX, 0.0); + + // Pixel distance from the closest point (Round Capsule SDF) + float dist = length(v_DistPixels - closestPoint); + + // SDF value: distance from the capsule edge + float sdf = dist - (v_LineWidth / 2.0); + + // Ultra-sharp edges (AA transition of 0.3 pixels total) + float alpha; + if (v_LineWidth >= 1.0) { + alpha = smoothstep(0.15, -0.15, sdf); + } else { + // Super thin lines: reduce opacity instead of shrinking width + float transverseAlpha = (1.0 - smoothstep(0.0, 1.0, abs(v_DistPixels.y))) * v_LineWidth; + alpha = transverseAlpha; + } + + // Aggressive fade for tiny segments far away to prevent blobbing + // If a segment is less than 0.8px on screen, fade it out to nothing + float lengthFade = clamp(v_LineLength / 0.8, 0.0, 1.0); + alpha *= lengthFade * lengthFade; // Quadratic falloff for tiny segments + + if (alpha <= 0.0) { + discard; + } + + vec4 color = vertexColor * ColorModulator; + color.a *= alpha; + + fragColor = apply_fog(color, sphericalVertexDistance, cylindricalVertexDistance, FogEnvironmentalStart, FogEnvironmentalEnd, FogRenderDistanceStart, FogRenderDistanceEnd, FogColor); +} diff --git a/src/main/resources/assets/lambda/shaders/core/advanced_lines.vsh b/src/main/resources/assets/lambda/shaders/core/advanced_lines.vsh new file mode 100644 index 000000000..46e84da2a --- /dev/null +++ b/src/main/resources/assets/lambda/shaders/core/advanced_lines.vsh @@ -0,0 +1,74 @@ +#version 330 + +#moj_import +#moj_import +#moj_import +#moj_import + +in vec3 Position; +in vec4 Color; +in vec3 Normal; +in float LineWidth; + +out vec4 vertexColor; +noperspective out float v_LineDist; +noperspective out float v_LineWidth; +noperspective out vec2 v_DistPixels; +noperspective out float v_LineLength; +out float sphericalVertexDistance; +out float cylindricalVertexDistance; + +const float VIEW_SHRINK = 1.0 - (1.0 / 256.0); + +void main() { + int vertexIndex = gl_VertexID % 4; + bool isStart = (vertexIndex < 2); + + float actualWidth = max(LineWidth, 0.1); + float padding = 0.5; // AA padding + float halfWidthExtended = actualWidth / 2.0 + padding; + + // Transform start and end + vec4 posStart = ProjMat * ModelViewMat * vec4(isStart ? Position : Position - Normal, 1.0); + vec4 posEnd = ProjMat * ModelViewMat * vec4(isStart ? Position + Normal : Position, 1.0); + + vec3 ndcStart = posStart.xyz / posStart.w; + vec3 ndcEnd = posEnd.xyz / posEnd.w; + + // Screen space coordinates + vec2 screenStart = (ndcStart.xy * 0.5 + 0.5) * ScreenSize; + vec2 screenEnd = (ndcEnd.xy * 0.5 + 0.5) * ScreenSize; + + vec2 delta = screenEnd - screenStart; + float lenPixels = length(delta); + + // Stable direction + vec2 lineDir = (lenPixels > 0.001) ? delta / lenPixels : vec2(1.0, 0.0); + vec2 lineNormal = vec2(-lineDir.y, lineDir.x); + + // Quad vertex layout + float side = (vertexIndex == 0 || vertexIndex == 3) ? -1.0 : 1.0; + float longitudinalSide = isStart ? -1.0 : 1.0; + + // Expansion in pixels: full radius + padding to contain capsule end + vec2 offsetPixels = lineNormal * side * halfWidthExtended + lineDir * longitudinalSide * halfWidthExtended; + + // Current point NDC + vec3 ndcThis = isStart ? ndcStart : ndcEnd; + float wThis = isStart ? posStart.w : posEnd.w; + + // Convert pixel offset back to NDC + vec2 offsetNDC = (offsetPixels / ScreenSize) * 2.0; + gl_Position = vec4((ndcThis + vec3(offsetNDC, 0.0)) * wThis, wThis); + + vertexColor = Color; + + // Pass coordinates for SDF + v_LineDist = side; + v_DistPixels = vec2(isStart ? -halfWidthExtended : lenPixels + halfWidthExtended, side * halfWidthExtended); + v_LineWidth = actualWidth; + v_LineLength = lenPixels; + + sphericalVertexDistance = fog_spherical_distance(Position); + cylindricalVertexDistance = fog_cylindrical_distance(Position); +} From 51713b625113c18e68a208c0b3b1efaf380f2cd6 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 20 Dec 2025 18:40:59 +0100 Subject: [PATCH 20/20] Remove unused pipelines --- .../graphics/mc/LambdaRenderPipelines.kt | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt index 8f3541d37..350a40a77 100644 --- a/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt +++ b/src/main/kotlin/com/lambda/graphics/mc/LambdaRenderPipelines.kt @@ -37,51 +37,6 @@ object LambdaRenderPipelines : Loadable { private val LAMBDA_ESP_SNIPPET = RenderPipeline.builder(RenderPipelines.TRANSFORMS_AND_PROJECTION_SNIPPET).buildSnippet() - /** - * Pipeline for static ESP faces (filled quads). - * - Translucent blending for see-through effect - * - No depth write to allow overlapping - * - No culling to see all faces - * - Uses TRIANGLES mode for maximum flexibility - */ - val ESP_FACES: RenderPipeline = - RenderPipelines.register( - RenderPipeline.builder(LAMBDA_ESP_SNIPPET) - .withLocation(Identifier.of("lambda", "pipeline/esp_faces")) - .withVertexShader(Identifier.ofVanilla("core/position_color")) - .withFragmentShader(Identifier.ofVanilla("core/position_color")) - .withBlend(BlendFunction.TRANSLUCENT) - .withDepthWrite(false) - .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) - .withCull(false) - .withVertexFormat( - VertexFormats.POSITION_COLOR, - VertexFormat.DrawMode.QUADS - ) - .build() - ) - - /** - * Pipeline for static ESP faces that render through walls. - * - Same as ESP_FACES but with no depth test - */ - val ESP_FACES_THROUGH: RenderPipeline = - RenderPipelines.register( - RenderPipeline.builder(LAMBDA_ESP_SNIPPET) - .withLocation(Identifier.of("lambda", "pipeline/esp_faces_through")) - .withVertexShader(Identifier.ofVanilla("core/position_color")) - .withFragmentShader(Identifier.ofVanilla("core/position_color")) - .withBlend(BlendFunction.TRANSLUCENT) - .withDepthWrite(false) - .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) - .withCull(false) - .withVertexFormat( - VertexFormats.POSITION_COLOR, - VertexFormat.DrawMode.QUADS - ) - .build() - ) - /** * Pipeline for ESP lines/outlines. * - Uses MC's line rendering with per-vertex line width